YAML Source is the Network Storage authoring standard.
Use the s&box Library Manager install for auto-updates. GitHub is best for agents, source review, contributions, and manual installs.

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.