Command Line Interface
Last updated: 6 August 2026
The PlayServ CLI (playserv-cli) scaffolds, deploys, and manages your server-side Functions from the terminal. It generates a ready-to-edit C# project, provisions the function's API key, packages the build, and ships it to the PlayServ FaaS gateway.
Install
The CLI is a .NET global tool.
- .NET 10 SDK
- Access to a PlayServ FaaS gateway (the managed cluster is the default — see Configuration)
dotnet tool install --global PlayServ.FaaS.Cli
Verify the install:
playserv-cli --help
Update later with dotnet tool update --global PlayServ.FaaS.Cli.
Quick start
Scaffold a function project
Start from a solution when you want several functions to share entities, contracts, and a single appsettings.Common.json. Use a standalone project for a single function.
- Solution
- Standalone
playserv-cli init --solution --name Acme --game-id <gameId> --function Hello
Creates ./Acme/ with a solution file, Acme.Shared.Entities, Acme.Shared.Contracts, appsettings.Common.json (carrying PlayServ:GameId), nuget.config, and an initial Function.Hello wired to the shared projects.
playserv-cli init --name PlayerProfile --game-id <gameId>
Generates ./Function.PlayerProfile/ from the bundled simple template (pass --template with-startup for a Startup class). The generated csproj carries <PlayServFunctionName>PlayerProfile</PlayServFunctionName> — that value identifies the function for deploy, update, and delete.
init auto-provisions an API key for the function and writes it to PlayServ:ApiKey in the project's appsettings.json. Standalone functions also get PlayServ:GameId; in-solution functions inherit it from appsettings.Common.json.
Write a handler
Handlers derive from PlayServHandler<TRequest> and return a HandlerResult. Constructor-inject services — the SDK host wires them up. Each project has exactly one handler.
using PlayServ.Sdk;
using PlayServ.Sdk.Handlers;
using PlayServ.Sdk.Persistence;
using Function.PlayerProfile.Entities;
using Function.PlayerProfile.Requests;
namespace Function.PlayerProfile;
public class PlayerHandler(
IRepository<Player> players,
ILogger<PlayerHandler> logger) : PlayServHandler<CreatePlayerRequest>
{
protected override async Task<HandlerResult> HandleAsync(CreatePlayerRequest request)
{
var player = new Player { Name = request.Name, Level = request.Level };
players.Add(player);
await players.SaveChangesAsync();
logger.LogInformation("Created player {PlayerId}", player.Id);
return HandlerResult.Ok(player);
}
}
Deploy
cd Function.PlayerProfile
playserv-cli deploy --tag v1.0.0
The function goes live at <GatewayUrl>/player-profile (the function name is lower-cased and kebab-cased on the wire).
Configuration
The CLI reads its settings from the FaaS section of appsettings.json (next to the installed tool), or from environment variables.
| Field | Required | Description |
|---|---|---|
GatewayUrl | Yes (pre-filled) | Address of the FaaS gateway. Ships pointing at the managed cluster (https://func.skeldar.playserv.io/run). Override here or with the FAAS_GATEWAY_URL env var to target a different gateway. |
TemplatesBaseUrl | No | Remote base URL for templates (<base>/<template>.zip). Empty → use bundled templates, or --src for a local folder. |
DisplayLogs | No | When true, also emit Microsoft.Extensions.Logging output alongside the CLI's styled messages. Default: false. |
The SDK that runs inside a deployed function ships with the same default GatewayUrl, so generated appsettings.json files intentionally omit PlayServ:GatewayUrl — they carry only PlayServ:ApiKey (and PlayServ:GameId for standalone functions). Add PlayServ:GatewayUrl only when targeting a non-default gateway.
Commands
Every command accepts the global --json flag (see Output modes).
init
Scaffold a single function, or a whole solution with shared projects.
- Function mode
- Solution mode
playserv-cli init --name <PascalCaseName> [--game-id <gameId>] \
[--template <simple|with-startup>] [--output <dir>] \
[--solution-path <file-or-dir>] [--src <path>] [--yes]
| Option | Required | Default | Description |
|---|---|---|---|
--name | Yes | — | PascalCase function name, written into <PlayServFunctionName>. |
--game-id | When standalone | — | Game identifier. Ignored in-solution (read from appsettings.Common.json). |
--template | No | simple | simple → minimal handler; with-startup → adds a Startup class. Standalone only. |
--output | No | ./Function.<Name> | Target directory (must not exist or be empty). |
--solution-path | No | auto-detect | Explicit .sln/.slnx file or a directory containing exactly one. |
--src | No | — | Local folder holding template folders. |
--yes | No | — | Auto-accept the API-key rename suggestion on a name collision. Required with --json. |
Standalone vs. in-solution is decided automatically: an explicit --solution-path wins; otherwise the CLI walks up from --output (up to 5 levels, or until .git) looking for a single solution file; if none is found it scaffolds a standalone project.
playserv-cli init --solution --name <PascalCaseName> --game-id <gameId> \
[--function <FnName>] [--output <dir>]
| Option | Required | Default | Description |
|---|---|---|---|
--solution | Yes | — | Selects solution mode. |
--name | Yes | — | PascalCase solution name → <Name>.sln/.slnx, plus <Name>.Shared.Entities and <Name>.Shared.Contracts. |
--game-id | Yes | — | Written into appsettings.Common.json under PlayServ:GameId. |
--function | No | — | Optional initial function. Omitted → the CLI confirms creating an empty solution. |
--output | No | ./<Name> | Target directory (must not exist or be empty). |
In every mode, init auto-provisions an API key labelled after the function (on a name collision it suggests <Fn>1, <Fn>2, … and prompts, or takes the suggestion with --yes).
deploy
Package the source as a ZIP, build it, and register a new function.
playserv-cli deploy [--tag <tag>] [--src <path>] [--namespace <ns>]
| Option | Default | Description |
|---|---|---|
--tag | latest | Image tag (e.g. v1.0.0, dev). |
--src | current dir | Source root (must contain a .csproj). |
--namespace | — | Target namespace. |
The function name comes from <PlayServFunctionName> in the nearest csproj. Deploy fails if a function with that name already exists — use update, or delete first.
update
Rebuild and roll out a new version of an already-deployed function. Options are identical to deploy.
playserv-cli update [--tag <tag>] [--src <path>] [--namespace <ns>]
publish
Solution-wide deploy/update — diffs every function project in the solution against the gateway and ships them in one pass. Refuses to run outside a solution.
playserv-cli publish [--deploy] [--update] [--parallel <N>] [--tag <tag>] \
[--namespace <ns>] [--dry-run] [--yes] [--solution-path <file>] [--src <path>]
| Option | Default | Description |
|---|---|---|
--deploy | — | Only ship functions absent from the gateway. |
--update | — | Only redeploy functions already on the gateway. Both omitted → ship everything. |
--parallel <N> | 1 | Bounded concurrency for build/upload. |
--tag <tag> | latest | Image tag applied to every function. |
--dry-run | — | Print the plan and exit — no prompts, no calls. |
--yes | — | Skip the confirmation prompt. Required with --json. |
Each Deploy (new) project must have a non-empty PlayServ:ApiKey in its appsettings.json, or the run fails with a hint to run init for that function.
delete
Remove a deployed function. The registry image is not deleted.
playserv-cli delete [--name <PascalCaseName>] [--src <path>]
Omit --name to resolve it from the csproj in --src or the current directory.
list
Show all deployed functions with status and invocation counts.
playserv-cli list
logs
Tail logs for a deployed function.
playserv-cli logs [--name <PascalCaseName>] [--src <path>] [--tail <lines>] [--since <ISO8601>]
| Option | Default | Description |
|---|---|---|
--name | csproj-derived | PascalCase function name. |
--tail | 100 | Number of most recent entries to fetch. |
--since | — | ISO 8601 timestamp; older entries are filtered out. |
API keys
Manage API keys scoped to a game.
playserv-cli apikey-provision --game-id <gameId> [--name <keyName>]
playserv-cli apikey-list --game-id <gameId>
playserv-cli apikey-revoke --game-id <gameId> --key-id <keyId>
config-set
Set game configuration parameters (currently EOS credentials).
playserv-cli config-set --game-id <gameId> \
[--eos-client-id <id>] [--eos-client-secret <secret>] [--eos-deployment-id <id>]
Output modes
By default every command prints styled output — tables, colors, icons, and human-friendly summaries. Errors use the same style on stderr with a red ✗ headline, a detail line, and a hint.
For scripting and CI, pass the global --json flag to switch to raw JSON on stdout. In this mode errors become a single JSON object on stderr with error, detail, and hint fields.
playserv-cli list # styled table
playserv-cli list --json # JSON, pipe into jq
Colors auto-disable when stdout isn't a terminal. You can also override them:
| Variable | Effect |
|---|---|
NO_COLOR=1 | Disable all ANSI colors. |
FORCE_COLOR=1 | Force colors on even when piped. |
PLAYSERV_CLI_NO_COLOR=1 | CLI-specific opt-out. |
Exit codes: 0 on success, 1 on error (details on stderr).
Typical workflows
- Single function
- Solution of functions
playserv-cli init --name Echo --game-id <gameId>
cd Function.Echo
playserv-cli deploy --tag v1.0.0 # ship it
playserv-cli list # verify
playserv-cli logs --tail 50 # tail while you invoke it
playserv-cli update --tag v1.1.0 # roll out a new version
playserv-cli delete # remove when done
playserv-cli init --solution --name Acme --game-id <gameId> --function Hello
cd Acme
# add more functions — they inherit shared projects + GameId
playserv-cli init --name World
playserv-cli init --name Leaderboard
# ship the whole solution in one pass (preview first)
playserv-cli publish --dry-run
playserv-cli publish --tag $CI_SHA --yes --parallel 4
New functions go through deploy; existing ones through update.
Troubleshooting
✗ Invalid argument.— a required option is missing or malformed. Runplayserv-cli --help; each command's hint line names the flag.✗ Cannot reach the PlayServ gateway.— verifyFaaS:GatewayUrlinappsettings.json(orFAAS_GATEWAY_URL) and that the gateway is running.✗ Function '<name>' already exists.—deployfound a live function with that name. Useupdateto redeploy, ordelete --name <name>first.
Next steps
- Functions — what server-side functions are and how they run
- API Key Management — project credentials and key types