Dedicated Server Runtime
Dedicated servers and backend hosts can call protected Network Storage flows with a server-side secret key. Do not bundle secret keys with a published game clie...
# Dedicated Server Runtime
Dedicated servers and backend hosts can call protected Network Storage flows with a server-side secret key. Do not bundle secret keys with a published game client.
## Key model
Normal game clients use:
- Project ID
- Public key (`sbox_ns_...`)
- Optional s&box auth/session/encrypted request security, depending on project settings
Dedicated servers can additionally send a secret key (`sbox_sk_...`) from the server process. A verified secret key is treated as trusted server/backend authentication for that request, so s&box auth tokens are not required and should not be sent.
Use the minimum execute permission needed:
| Runtime use | Required permission |
|-------------|---------------------|
| Secret-gated endpoints | `endpoints: x` |
| Secret-gated queries | `queries: x` |
| Direct collection row/document API | `collections: x` |
Read/write permissions (`r`, `rw`) are for editor sync and management APIs. Execute (`x`) is for runtime calls from dedicated servers/backends.
## Library setup
Pass the secret key to the dedicated server process, not to clients:
```bash
sbox-server.exe +game your.org.game your.map +network_storage_secret_key sbox_sk_your_secret_key
```
The primary launch key is `+network_storage_secret_key`; use that in new deployments. The library also accepts these namespaced aliases on dedicated server hosts:
- `+network-storage-secret-key`
- `+sboxcool_secret_key`
- `+networkStorageSecretKey`
- `+sboxcoolSecretKey`
- `+nsSecretKey`
- `+ns_secret_key`
Generic launch keys such as `+secret-key`, `+secret_key`, and `+secretKey` are intentionally not supported to avoid colliding with other libraries. The published client should still contain only the project ID and public key.
## What is sent over HTTP
When a dedicated secret is active:
- Endpoint calls from the official library send the secret key as `x-secret-key` and the public key as `x-public-key`.
- Query calls from the official library send the secret key as `x-api-key` and the public key as `x-public-key`. Raw HTTP integrations may also use `x-secret-key` with a public key in `x-api-key`.
- Direct collection row/document calls send the secret key as `x-api-key` and the public key as `x-public-key`.
- An internal secret-key URL flag may appear on endpoint requests; the actual secret key is never placed in the URL by the library.
- s&box auth tokens and auth-session tokens are not required for that request.
The backend skips s&box auth/session requirements after the secret key is verified.
## Endpoints
Enable **Requires secret key** on endpoints that should only be callable by dedicated servers or backend systems.
```csharp
var result = await NetworkStorage.CallEndpoint( "settle-match", new
{
matchId = "match_123",
winnerSteamId = "76561198000000001"
} );
```
A verified secret key exposes these runtime variables to endpoint/workflow steps:
- `_hasSecretKey` — `true` when the request included a valid secret key
- `_isDedicatedServer` — alias of `_hasSecretKey`
## Queries
Secret-gated queries require `queries: x`.
```csharp
var leaderboard = await NetworkStorage.RunQuery( "top_players" );
```
Queries that require a secret key are not published as public CDN JSON snapshots.
Query definitions can also use advanced server-side joins and computed fields. Add each foreign collection as a source, give it an alias, then join by any field and sort/output calculated values:
```json
{
"joins": [
{ "source": "fish", "left": "player.fishType", "right": "fishType" },
{ "source": "rarity", "left": "player.fishRarity", "right": "rarity" }
],
"computedFields": [
{
"name": "totalFishValue",
"label": "Total fish value",
"expression": "{{player.fishWeight}} * {{fish.fishValuePerKg}} * {{rarity.modifier}}"
}
],
"field": "totalFishValue",
"fieldLabel": "Total fish value",
"fields": [
{ "name": "fish", "label": "Fish", "path": "player.fishType" },
{ "name": "valuePerKg", "label": "Value/kg", "path": "fish.fishValuePerKg" },
{ "name": "total", "label": "Total", "path": "totalFishValue" }
]
}
```
Expressions use the same safe `{{...}}` template/math evaluator as endpoints and can reference joined source aliases plus `values.*` constants/tables.
## Collection row/document API
Direct collection row/document reads and writes require `collections: x` when called with a secret key.
```csharp
await NetworkStorage.SaveDocument( "players", steamId, new { xp = 100 } );
await NetworkStorage.UpdateDocument( "players", steamId,
NetworkStorageOperation.Increment( "xp", 25, source: "dedicated-server", reason: "Match reward" ) );
await NetworkStorage.DeleteDocument( "players", steamId );
```
Secret keys with `collections: x` may access endpoint-controlled collections through the direct row/document API. `DeleteDocument` and `DeleteRecord` delete individual rows/documents only; with a verified dedicated secret they can be used for diagnostics/backoffice cleanup even when public record deletion is disabled.
## Collection definition deletion
Collection definitions/schemas cannot be deleted through the library, Sync Tool, or Management API. Delete collections from the website dashboard so the two-step verification flow can run.
Deleting individual collection rows/documents remains supported through the runtime API. Public/client deletes require record deletion to be enabled on the collection; dedicated secret keys with `collections: x` may delete rows for server/backoffice cleanup.
## Validation checklist
- Keep `sbox_sk_...` out of client code and published config.
- Use a separate runtime secret key from your editor/CI sync key.
- Grant only the execute scopes needed by the server.
- Enable **Requires secret key** on protected endpoints/queries.
- Use `_hasSecretKey` or `_isDedicatedServer` in workflows when branching between client and dedicated-server paths.