Skip to content

Repository files navigation

Network Storage for s&box

Persistent cloud storage, server-side endpoints, and an editor sync tool for s&box games — powered by the sboxcool.com API.

Useful Links

Features

  • Runtime Client — Call server endpoints, fetch game values, read/write documents from your game code
  • Editor Sync Tool — Push and pull collections and endpoints between local YAML source files and the sboxcool.com dashboard
  • Setup Wizard — Editor window for entering and validating your API credentials
  • Diff Viewer — Side-by-side comparison of local vs remote data before syncing
  • Network Logger — Ring buffer that captures all API traffic for in-game debug panels
  • JSON Helpers — Extension methods for reading API response payloads

Installation

From s&box (recommended)

Install directly from the s&box asset browser:

https://sbox.game/sboxcool/network-storage

Or find it in the editor:

  1. Open the s&box editor
  2. Go to Editor > Library Manager
  3. Search for "Network Storage"
  4. Look for the one published by sboxcool — do not install from other authors
  5. Click Add to Project

The library appears in your project's Libraries/ folder automatically.

Manual (git clone)

Clone this repo into your project's Libraries/ directory:

cd "YourProject/Libraries"
git clone https://github.com/sbox-cool/sbox-network-storage "Network Storage by sboxcool"

Or add it as a git submodule:

git submodule add https://github.com/sbox-cool/sbox-network-storage "Libraries/Network Storage by sboxcool"

Setup

1. Create a project on sboxcool.com

  1. Go to sboxcool.com and create a new project
  2. Note your Project ID from the dashboard
  3. Create API keys:
    • Public key (sbox_ns_ prefix) — used by the game client at runtime
    • Secret key (sbox_sk_ prefix) — used by the editor sync tool only, never ships with your game

2. Configure in the s&box editor

  1. Open Editor > Network Storage > Setup
  2. Enter your Project ID, Public API Key, and Secret Key
  3. Click Save Configuration
  4. Click Test Connection to verify

Your credentials are saved to Editor/Network Storage/.env — this file is in the Editor/ directory which s&box excludes from publishing. Your secret key never ships with your game.

Dedicated server endpoint secret (optional)

Dedicated servers can supply a runtime endpoint secret without putting it in the published bundle:

sbox-server.exe +game your.org.game your.map +hostname "My Server" +network_storage_secret_key sbox_sk_your_secret_key

The primary dedicated launch key is +network_storage_secret_key. The library also accepts aliases (+network-storage-secret-key, +sboxcool_secret_key, +networkStorageSecretKey, +sboxcoolSecretKey, +nsSecretKey, +ns_secret_key) on Application.IsDedicatedServer hosts, and validates the supplied key at game startup. Generic names such as +secret-key, +secret_key, and +secretKey are intentionally not supported. When the dedicated key is used, the library does not request or send s&box auth tokens. Endpoint requests use an internal backend flag while the actual launch secret travels in the x-secret-key HTTPS header. Storage document row reads/writes/deletes use the secret as the HTTPS x-api-key header without a URL secret flag so secret keys with collections:execute can access endpoint-only collections. Do not put sbox_sk_ values in code, public credentials, or client-callable URLs. Collection schema deletion remains website-only.

Quick Start

Create a config class in your game project:

namespace Sandbox;

public static class MyNetStorageConfig
{
    public const string ProjectId = "your_project_id";
    public const string ApiKey = "sbox_ns_your_public_key";

    public static void Initialize()
    {
        NetworkStorage.Configure( ProjectId, ApiKey );
    }
}

Then call endpoints from your game code:

// Configure once at startup
if ( !NetworkStorage.IsConfigured )
    MyNetStorageConfig.Initialize();

// Call a server endpoint (GET)
var player = await NetworkStorage.CallEndpoint( "load-player" );

// Call with input (POST)
var result = await NetworkStorage.CallEndpoint( "mine-ore", new
{
    ore_id = "iron",
    kg = 5.0f
} );

// Endpoint URLs from the dashboard are also accepted; the slug is extracted.
var sameResult = await NetworkStorage.CallEndpoint(
    "https://api.sboxcool.com/v3/endpoints/your_project_id/mine-ore?apiKey=sbox_ns_your_public_key",
    new { ore_id = "iron", kg = 5.0f } );

// Update collection documents directly (dedicated servers attach +network_storage_secret_key automatically)
await NetworkStorage.SaveDocument( "player-data", Game.SteamId.ToString(), new { level = 2, xp = 100 } );
await NetworkStorage.UpdateDocument( "player-data", Game.SteamId.ToString(),
    NetworkStorageOperation.Increment( "xp", 50, source: "server", reason: "quest" ),
    NetworkStorageOperation.Set( "lastSeen", DateTimeOffset.UtcNow.ToUnixTimeSeconds() ) );
await NetworkStorage.DeleteDocument( "player-data", Game.SteamId.ToString() );

// Read the response
if ( result.HasValue )
{
    var currency = JsonHelpers.GetInt( result.Value, "currency", 0 );
    var level = JsonHelpers.GetInt( result.Value, "level", 1 );
}

See the Examples/ folder for complete working patterns.

Runtime API Reference

NetworkStorage (static)

Method Description
Configure(projectId, apiKey) Set credentials. Call once at startup.
CallEndpoint(slugOrUrl, input?) Call a server endpoint by slug or endpoint URL. Returns JsonElement?.
GetGameValues() Fetch all game values (constants + tables). Returns JsonElement?.
GetDocument(collectionId, documentId?) Read a document from a collection. Defaults to current player's Steam ID.
SaveDocument(collectionId, documentId, data) Save/replace a collection document. Dedicated servers attach the configured secret key automatically.
UpdateDocument(collectionId, documentId, ops) Apply server-side operations (set, inc, push, pull, remove) to a document.
DeleteDocument(collectionId, documentId?) Delete one collection document/row. Public calls require record deletes to be enabled; dedicated secret keys with collection execute permission can delete rows for diagnostics/backoffice cleanup. Does not delete collection schemas.
ListRecords/CreateRecord/RenameRecord/DeleteRecord Manage multi-record save slots.
NetworkStorageAnalytics.TrackEvent(eventType, payload?) Report an allowlisted custom Player Analytics event.
NetworkStorageAnalytics.Warning(code, message?, context?) Report a recoverable warning/pain point to the player timeline.
NetworkStorageAnalytics.Error(exception, code?, context?) Report an exception/error to the player timeline without breaking gameplay.
NetworkStorageAnalytics.SessionStart/SessionEnd Report managed session boundaries when analytics is enabled.
IsConfigured true after Configure() has been called.
ApiRoot The full versioned API URL (e.g. https://api.sboxcool.com/v3).

See analytics.md for Player Analytics setup, custom events, warnings, and error reporting examples.

JsonHelpers (static)

Safe extraction from API JsonElement payloads with caller-provided defaults. Handles missing keys and string-to-number coercion.

var name = JsonHelpers.GetString( data, "playerName", "Unknown" );
var level = JsonHelpers.GetInt( data, "level", 1 );
var speed = JsonHelpers.GetFloat( data, "speed", 1.0f );
var active = JsonHelpers.GetBool( data, "active", true );

Extension Methods (on JsonElement)

Shorthand wrappers around JsonHelpers, plus collection parsers:

// Shorthand
var name = data.Str( "playerName", "Unknown" );
var level = data.Int( "level", 1 );
var speed = data.Float( "speed", 1.0f );

// Parse arrays
var upgrades = data.ReadStringList( "purchasedUpgrades" );

// Parse objects
var ores = data.ReadDictionary( "ores", v => (float)v.GetDouble() );

// Parse table rows
var items = data.ReadList( "rows", row => new ItemInfo(
    row.Str( "id" ), row.Str( "name" ), row.Int( "tier" )
) );

SaveStateTracker

Wraps endpoint calls with automatic state management for HUD feedback:

var tracker = new SaveStateTracker();

// Simple tracked call
var result = await tracker.Call( "mine-ore", new { ore_id = "iron", kg = 5 } );
// tracker.State is now Saved or Error
// tracker.IsBusy is true while any call is in flight

// Optimistic update with auto-revert
await tracker.CallAndApply( "sell-ore", input,
    applyOptimistic: () => { /* update local state */ },
    applyServer: (data) => { /* apply authoritative response */ },
    revert: () => { /* undo on failure */ }
);

NetLog

Static ring buffer capturing all Network Storage events. Use it for debug panels:

// Entries are added automatically by NetworkStorage
foreach ( var entry in NetLog.Entries )
{
    // entry.Time, entry.Kind (Request/Response/Error/Info),
    // entry.Tag, entry.Message
}

// Add custom entries
NetLog.Info( "my-system", "Custom log message" );

// Track changes (for UI refresh)
var version = NetLog.Version; // increments on every add/clear

Editor Sync Tool

The Sync Tool lets you manage your sboxcool.com project data as local YAML source files, then push/pull changes through the API. YAML is the preferred authoring format. Existing legacy JSON resources remain readable and pushable for compatibility; newly created and pulled resources are written as YAML.

Open the Sync Tool

Editor > Network Storage > Sync Tool

Workflow

  1. Define new collections, endpoints, workflows, tests, and libraries as YAML source files in Editor/Network Storage/
  2. Existing JSON resources can still be pushed; migrate them to YAML when convenient to use the current authoring format
  3. Click Check for Updates to compare local files against the remote server
  4. Push sends your local changes to sboxcool.com
  5. Pull downloads the latest from sboxcool.com to your local files
  6. View Diff shows a side-by-side comparison before overwriting

Source Authoring

YAML source files use kind-specific names:

collections/<id>.collection.yml
endpoints/<slug>.endpoint.yml
workflows/<id>.workflow.yml
tests/<id>.test.yml
libraries/<id>.library.yml

.yaml is also accepted, but project documentation and generated examples should prefer .yml. Legacy JSON files are unsupported and are not automatically reverse-converted into YAML.

Each source file starts with:

sourceVersion: 1
kind: endpoint
id: mine-ore
definition:
  method: POST
  steps: []

See source-authoring.md, source-authoring.schema.json, and Examples/SourceAuthoring/ for the current source model and examples.

Validate and preview source files locally:

python Libraries/sboxcool.network-storage/Editor/source_compiler.py --project-root .
python Libraries/sboxcool.network-storage/Editor/sync.py --project-root . --sources --dry-run

Status Indicators

Icon Meaning
In sync — local matches remote
Local only — exists locally but not on server
Remote only — exists on server but not locally
Differs — local and remote have different content

Data Folder Structure

Editor/
  Network Storage/              # Configurable in Setup
    .env                        # Credentials (gitignored, never published)
    collections/                # YAML source collections
      player_data.collection.yml
      game_values.collection.yml
    endpoints/                  # YAML source endpoints
      load-player.endpoint.yml
      mine-ore.endpoint.yml
      sell-ore.endpoint.yml
    workflows/
      check-currency.workflow.yml
    libraries/
      economy.library.yml

Security

  • Secret key (sbox_sk_) is stored in Editor/Network Storage/.env — the Editor/ directory is excluded from s&box publishing, so your secret key never ships with your game
  • Public key (sbox_ns_) is safe to include in your game code — it can only be used with Steam authentication
  • The .env file is gitignored by default — never commit it to version control
  • See .env.example for the expected format

Data Source Mode

Network Storage runs in API Only mode. Runtime reads and editor sync operations use the API; local JSON files and API fallback modes are unsupported.

MCP Server (for AI Agents)

This repo includes an MCP (Model Context Protocol) server that gives AI coding agents like Claude deep knowledge of the Network Storage system — collections, endpoints, workflows, template syntax, error diagnosis, and more.

What It Provides

  • 10 tools — validate source files, scaffold new collections/endpoints/workflows, get documentation, diagnose errors
  • 8 resources — all documentation files exposed for contextual reading

Setup

The MCP server requires Bun to be installed.

  1. The .mcp.json file at the repo root auto-configures Claude Code to use the MCP server
  2. Dependencies are installed automatically on first launch (bun install)
  3. Open Claude Code in this repo and the MCP will be available

To verify it's working, run /mcp in Claude Code — you should see sbox-network-storage listed.

Manual Setup (other MCP clients)

If your MCP client doesn't auto-discover .mcp.json, add this to your MCP configuration:

{
  "mcpServers": {
    "sbox-network-storage": {
      "command": "bash",
      "args": ["-c", "cd mcp && bun install —silent && cd .. && bun run mcp/index.ts"]
    }
  }
}

Set the working directory to this repository root.

Available Tools

Tool Description
get_documentation Retrieve docs by topic (collections, endpoints, workflows, setup, errors, etc.)
validate_collection Validate a collection definition for correct schema and naming
validate_endpoint Validate an endpoint definition — steps, operators, templates, constraints
validate_workflow Validate a workflow definition — conditions, onFail config
scaffold_collection Generate a collection YAML source template
scaffold_endpoint Generate an endpoint YAML source template
scaffold_workflow Generate a workflow YAML source template
get_examples Get examples for common game scenarios (inventory, currency, leaderboard, etc.)
validate_env_config Validate .env credential file format
diagnose_error Diagnose s&box console errors and suggest fixes

License

MIT — see LICENSE.

About

Cloud storage library, server-side endpoints and in-editor sync for s&box games.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages