# Configuration

Server config fields, defaults, visible-player filtering and hot reload

Source: https://socketsvc.vercel.app/server/configuration
## Config file location

| Server side | Config file |
| --- | --- |
| Paper plugin | `plugins/SocketSVC/config.yml` |
| Fabric/Quilt/NeoForge/Forge server mod | `config/socketsvc-server.yml` |

The default config is written on first start. Both files have the same fields and can be copied between each other. Defaults come from [config.yml](https://github.com/HippoDev03/SocketSVC/blob/main/server/paper/src/main/resources/config.yml) and [socketsvc-server.yml](https://github.com/HippoDev03/SocketSVC/blob/main/server/common/src/main/resources/socketsvc-server.yml) (they must match, which `ServerModConfigTest` checks); the only difference is that `metrics` currently exists only in the Paper plugin.

## Fields

| Field | Default | Behaviour |
| --- | --- | --- |
| `port` | `24455` | Port of the WebSocket listener, always a plain (non-TLS) local port; reload rebinds it in the background without restarting Minecraft |
| `bind-address` | `0.0.0.0` | Network interface the listener binds; `0.0.0.0` keeps direct/LAN access, use `127.0.0.1` when the reverse proxy runs on the same machine |
| `public-url` | `""` | WebSocket base URL sent to clients; no trailing `/`, query or `/voice` path (the client appends `/voice?token=...&uuid=...`) |
| `public-host` | `localhost` | Used only when `public-url` is blank, to build `ws://<public-host>:<port>` |
| `hearing-range` | `48.0` | Hearing distance in blocks within the same world; players in different worlds never hear each other |
| `fade-start-distance` | `0.0` | Distance where linear volume fade begins, silent at `hearing-range`; 0 fades over the whole range |
| `spectator-interaction` | `false` | Whether spectators can speak to non-spectators |
| `visible-players-only` | `false` | When true, listeners only receive voice from players they track and are allowed to see, including microphone audio relayed through spatial speakers; see [Visible players only](#visible-players-only) |
| `soundboard-enabled` | `true` | When false, the server drops all soundboard audio |
| `server-list-advert` | `true` | Marks this server's status ping so a player running the client mod sees a voice-chat badge next to it in the multiplayer server list, before joining; see [Server list badge](#server-list-badge) |
| `addons.uses` | empty | Addons this server uses, offered to clients that have the mod; see [Telling clients what you use](#telling-clients-what-you-use). Server-side addon jars live in `addons/` instead, see [Server addons](#server-addons) |
| `addons.required` | empty | Addons clients install for themselves on connect, from their own index; see [Installing it for them](#installing-it-for-them) |
| `addons.root-commands.*` | off | Gives each server addon a command of its own, like `/cinema`; see [Root commands](#root-commands) |
| `require-client.*` | off | Refuse players who are not running the client mod; see [Requiring the client mod](#requiring-the-client-mod) |
| `voice-ranges.*` | off | Whisper and shout — a quieter tier and a louder one on a key each; see [Whisper and shout](#whisper-and-shout) |
| `range-overrides.*` | off | Per-world and per-region hearing ranges, for when one number cannot describe every place on the server; see [Per-world and per-region ranges](#per-world-and-per-region-ranges) |
| `groups.*` | off | [Voice groups](/server/groups): named channels players join from the in-game menu. Disabled by default, and while disabled the capability is not even negotiated, so clients know to say so |
| `metrics.enabled` | `true` | Paper plugin only: anonymous, aggregate usage metrics (server count, versions, platform), never player identities or voice data; set to false to opt out, takes effect on the next server start |

Every field except `metrics.enabled` supports `/ssvc-server reload`; see [Hot reload](#hot-reload).

### Field notes and invalid values

- **`spectator-interaction`**: when false, spectators can still hear non-spectators and talk to other spectators; same-world and distance limits apply in either mode.
- **`hearing-range`**: non-finite or non-positive values disable voice relay.
- **`fade-start-distance`**: must be at least 0 and less than `hearing-range`. If an existing config lacks this field or the value is invalid, the legacy 70% start (`hearing-range × 0.7`) is kept, so upgrading does not change an existing fade curve. This is a SocketSVC-specific option, not a Simple Voice Chat config key.
- **`port`** must be an integer from 1 to 65535; **`bind-address`** must not be blank.
- **`public-url`** must be a `ws://` or `wss://` URL without user info, query or fragment; **`public-host`** must not contain `/`, `@`, `?`, `#` or whitespace.
- **`server-list-advert`**: advisory only. Nothing about joining, handshaking or talking depends on it, and a server that does not advertise simply shows no badge — the client never claims the opposite of what it found.
- **`voice-ranges`** is validated on every reload whether or not a tier is switched on, the same as `range-overrides` below.
- **`range-overrides`** is validated on every reload whether or not it is switched on, so a typo is refused by the reload that introduced it rather than the day somebody flips the switch.
- When `soundboard-enabled` is false, the server still negotiates the soundboard protocol so clients cannot bypass the switch through the legacy microphone fallback; dedicated soundboard frames are dropped by the server instead. See [Voice protocol](/server/voice-protocol#permission).

## Telling clients what you use

A server can name the addons it runs. Players with the client mod see them on their **Addons** tab under "This server uses", with an install button for anything they are missing.

```yaml title="plugins/SocketSVC/config.yml"
addons:
  uses:
    - cinema
    - walkie-talkie
```

`/ssvc-server reload` applies a change without reconnecting anybody, and `/ssvc-server status` lists what is being advertised.

<Callout type="warn" title="Ids only — and the wire format has nowhere to put anything else">
  You name an addon; the client resolves that name against the [index it configured itself](/addons/using#where-addons-come-from) and downloads from there. A server handing a client a jar location would be remote code execution with a progress bar, so the message is a space-separated list of ids and an id cannot contain `/` or `:`.

  An id you ask for that a player's index does not offer shows on their screen as **Not in your index**, rather than as a button that cannot work. Where to get it is not yours to say.
</Callout>

### Installing it for them

`uses` is an offer: a row on the tab with an install button. `required` is the same offer with the button pressed for the player.

```yaml title="plugins/SocketSVC/config.yml"
addons:
  required:
    - cinema
```

A client that connects without one of these downloads it from its own index, switches it on, and says so in chat; one that has an older build than the index offers is updated the same way — no tab to find. One already installed but switched off is switched on again, and that is said too. Anything listed here is advertised as used as well, so a client too old to understand the second line still sees the offer.

What it does not change: the source is still the index **the player** configured, an id their index does not carry is reported in chat rather than fetched from anywhere else, and a player can turn the automatic install off on their Addons tab — in which case chat tells them what you require and leaves the install to them. A mod still cannot load until they restart.

### Nothing here refuses anybody

A player who ends up with none of it plays and talks normally — the only thing they miss is whatever those addons do. There is no switch that kicks a player for lacking an addon: refusing players is what [`require-client`](#requiring-the-client-mod) is for, and it is about the voice mod itself.

An **addon** can be switched on as soon as it downloads. A **mod** cannot load until that player restarts their game, so the session that offered it goes without — and their screen says so rather than implying otherwise.

An id that is not a usable addon id is refused by the reload that introduced it, with the running settings left untouched, rather than being advertised to clients that could never resolve it.

## Server addons

Some features ship as **server addons**: plain jars you drop in `plugins/SocketSVC/addons/` (or `config/socketsvc/addons/` for the server mods). One jar works on Paper, Fabric, NeoForge and Forge — a server addon never touches a Bukkit or Minecraft type, so there is nothing to build four times.

```text
plugins/SocketSVC/
  config.yml
  addons/
    socketsvc-addon-cinema-server-0.2.0.jar
    cinema.yml
```

Each addon reads its own `<id>.yml` beside its jar, rather than a block in `config.yml`: an addon you delete should take its settings with it, and `config.yml` should not describe things that are no longer installed.

They load at startup and again on `/ssvc-server reload`, which re-reads the folder — so adding or removing one needs no restart. They are deliberately **not** hot-swapped mid-session the way client addons are: a server addon holds state other people are connected to.

```text
/ssvc-server addon <id> [arguments...]
```

reaches an addon's own commands, behind `socketsvc.admin.addon` (defaults to op). `/ssvc-server status` lists what loaded, and names anything that did not along with why.

### Root commands

Turned on, each addon that takes commands also gets a command of its own, so `/cinema play lobby` does what `/ssvc-server addon cinema play lobby` does:

```yaml
addons:
  root-commands:
    enabled: true
    names:
      cinema: film      # call it /film instead
      walkie-talkie: '' # or give one none
```

**Off by default.** A command name like `/cinema` is one every other plugin and mod on the server could also want, so claiming it is your decision rather than an addon's.

- Each addon's command is named after its id unless `names` says otherwise.
- **A name something else already has is refused, not taken over.** The server log says which addon missed out and who has the name; rename it under `names`. The long form always works either way.
- It is the same command behind a shorter name: the same arguments, the same tab completion, the same `socketsvc.admin.addon` permission. Players without that permission do not see it at all.
- Changes apply on `/ssvc-server reload`, with no restart, and players' tab lists update straight away. On the server mods a root command that is turned off stays in the game's command list but is hidden from everyone and cannot be run — the game has no way to remove one while it runs.

<Callout title="A broken jar costs that addon and nothing else">
  An addon that fails to load is reported and skipped; the server still starts, and the other addons still load. Nothing you drop in that folder can stop players getting in.
</Callout>

The [cinema](/addons/cinema) is the worked example: its server jar defines screens and drives their playheads, while SocketSVC itself carries only the timecode sync.

## Microphone tap

Lets server plugins receive players' voice as it arrives — a bridge to another voice mod, a Discord relay, recording, transcription or moderation replay. The API is documented under [Microphone tap](/server/spatial-audio-api#microphone-tap-api-version-4). The [Cross System Bridge](/server/cross-system-bridge) needs it to carry SocketSVC players' voices to Simple Voice Chat.

```yaml title="plugins/SocketSVC/config.yml"
microphone-tap:
  enabled: false
```

<Callout type="warn" title="Off by default, and this default is about your players">
  Every other switch in this file decides what SocketSVC does with audio. This one decides whether audio leaves SocketSVC at all, for code this project did not write. Turn it on because you installed something that needs it and you know what that something does with what it hears.
</Callout>

While it is off, a plugin asking to subscribe is **refused with a reason** rather than accepted and given silence, so an addon author finds out immediately that the switch is the problem.

### Who can be heard

`socketsvc.bridge` decides whose audio may leave, player by player. It defaults to `true` — the switch above is what keeps the feature off, so a node defaulting to false would leave you turning this on and still hearing nothing. Take it away from anybody whose voice should never leave SocketSVC. It is checked per frame against an answer refreshed every second, so revoking it stops their audio within a second rather than at their next reconnect.

A tap hears exactly what the room hears: frames reach it only after the mute check and after `socketsvc.speak`, so a muted or silenced player is silent to a tap too. Soundboard and instrument audio are not tapped.

### Seeing what is listening

Every plugin that subscribes is **named in your server log** the first time it does. That log line is the only way to see what is listening without reading plugin source, so read it after turning this on.

`/ssvc-server reload` applies a change. Turning it back off closes every live subscription rather than leaving one running against a decision you have just made; changing the port does **not** end a subscription, so a rebind cannot silently break a plugin that was working.

## Requiring the client mod

A server can refuse players who are not running SocketSVC, with a message pointing them at the download.

```yaml title="plugins/SocketSVC/config.yml"
require-client:
  enabled: false
  grace-seconds: 10
  message: "This server requires the SocketSVC voice chat mod.\nDownload it at https://modrinth.com/mod/socketsvc"
```

`message` is the disconnect reason. It accepts [MiniMessage](/server/messages#formatting) and `&` colour codes, and `\n` starts a new line. Every other message the server sends is in the [messages file](/server/messages).

<Callout type="warn" title="Off by default, and this default matters more than the others">
  Every other switch in this file degrades a feature when it is wrong. This one closes the door on people who were already playing on your server — upgrade the jar, restart, and your regulars cannot get in. Nothing is ever refused unless you turn it on yourself, and a server with it off does not delay a single join.
</Callout>

### How a client is detected

By the plugin channel the handshake travels on. A client that registers it has the mod — the same question the server already asks before it sends a handshake at all.

It is deliberately **not** "do they have a voice connection". That also depends on your voice port being reachable from where the player is, so using it would kick people because your tunnel was down, which is the worst thing this feature could do.

### Nobody is refused for not having answered yet

There are three states, not two: **has the mod**, **does not have it**, and **has not said yet**. Channel registration lands shortly after somebody joins rather than at the instant they do, so for a moment the honest answer is that nobody knows — and `grace-seconds` is how long that moment is allowed to last. A player is only refused once it has passed with nothing registered.

The default of 10 seconds is mostly slack: registration normally arrives within a second. The range is 1–120.

### Exemption

`socketsvc.require.exempt` (default op) is never refused, so **turning the requirement on cannot lock out the person who turned it on** — and a staff member on a laptop without the mod can still get in and turn it off again.

Every refusal is logged with the player's name and the reason. "I cannot join your server" is a report you have to be able to explain, and a kick with no trace of why is how this ends up blamed on the voice port.

<Callout type="error" title="Geyser and Floodgate players can never pass this check">
  Bedrock players cannot install the mod ([Bedrock support](/server#support-matrix)). Turning this on with Geyser installed is telling every Bedrock player to leave. That may be exactly what you want; it should not be a surprise.
</Callout>

### For addons

`SocketSvcApi` and `SocketSvcModApi` expose the same registry from version 3:

```java
VoiceClientState state = api.voiceClientState(player); // PRESENT, ABSENT or UNKNOWN
boolean hasMod = api.hasVoiceClient(player);           // PRESENT only
boolean enforced = api.requiresVoiceClient();
```

An addon that acts on `ABSENT` should decide what it does about `UNKNOWN` rather than treating it as a no — `hasVoiceClient` collapses both to `false`, which is convenient for showing an icon and wrong for deciding whether to refuse somebody.

`/ssvc-server status` reports whether the requirement is on and how many connected players are known to have the mod.

## Whisper and shout

A quieter tier and a louder one, each on a key the player holds. **Both are off by default** — they change how far every player on the server can be heard, which is a decision about how a server plays rather than something to hand out unasked.

```yaml title="plugins/SocketSVC/config.yml"
voice-ranges:
  whisper:
    enabled: true
    hearing-range: 8.0
  shout:
    enabled: true
    hearing-range: 96.0
```

Each tier is switchable on its own — a server that wants whisper but not shout is an ordinary thing to want — and `/ssvc-server reload` applies a change immediately, without reconnecting anybody.

### A tier scales the range, it does not replace it

Each tier is written as a distance because that is what an operator can picture, and applied as a **ratio** to whatever range is in force where the speaker is standing.

With `hearing-range: 48` and a shout of `96`, a shout carries twice as far as normal. Inside a [`range-overrides`](#per-world-and-per-region-ranges) arena whose range is 16, it therefore carries 32 — not 96. Replacing instead of scaling would mean every quiet place on the server had a shout that ignored it, which would make per-world ranges meaningless to anybody holding a key.

A range of 0 stays silent whatever tier is used: silence scaled by anything is still silence.

### A tier changes distance and nothing else

A shout does not cross worlds, pass the `visible-players-only` rule, or reach somebody who has deafened themselves. A whisper is not a private channel — it is a smaller radius, and anybody standing inside it hears you. [Voice groups](/server/groups) are what a real private channel is for.

Soundboard clips and [plugin speakers](/server/spatial-audio-api) are never whispered or shouted: a tier is how loudly somebody is *talking*, and a clip is not speech.

### You can see who is whispering and who is shouting

The voice icon beside a player's nameplate is drawn **smaller for a whisper and larger for a shout**, so the tier is readable at a glance without a second icon beside it to compare against. Your own icon on the HUD scales by the same amounts, so what you see on yourself is what everybody else sees on you.

The tier rides the player roster alongside the deafen state, so it reaches every client the same way. A tier the server has switched off is never published: a client one reload ahead of the operator would otherwise draw somebody as whispering while the relay routes them at ordinary range.

### Keys, and what a client does when a tier is off

Both keybinds ship **unbound**. Both tiers are off by default server-side, so a server that switches one on will tell its players which key to bind; claiming two more default keys for a feature most servers never enable is a cost paid by everybody for the benefit of a few. Players bind them under Controls → SocketSVC. Holding both keys at once whispers — it is the quieter of the two, and the one whose point is not being overheard.

The server tells each client which tiers exist here, so a key for a tier that is off does nothing and says so, once, in chat. Without that the player's own HUD would say "Whisper" while everybody heard them at ordinary volume — a silent disagreement between two machines that both think they are right.

<Callout title="Older clients are unaffected">
  A client that predates whisper and shout never sends a tier and is heard normally. A client that sends a tier the server has since switched off is heard normally too, rather than being silenced or disconnected — a client whose idea of the config is one reload out of date should not lose its voice over it. See [Voice protocol](/server/voice-protocol#whisper-and-shout).
</Callout>

`/ssvc-server status` reports each tier, its configured distance and the ratio it works out to.

## Per-world and per-region ranges

`hearing-range` is one number for the whole server, which cannot describe a network with a lobby, a survival world and a minigame arena on it. `range-overrides` names the exceptions; anything it does not name keeps the global range.

```yaml title="plugins/SocketSVC/config.yml"
range-overrides:
  enabled: true

  worlds:
    lobby:
      hearing-range: 0          # no voice in the lobby at all
    arena:
      hearing-range: 16.0
      fade-start-distance: 8.0

  regions:
    - name: library
      world: world
      min: [100, 60, 100]
      max: [140, 80, 140]
      hearing-range: 8.0
```

On the server mods, worlds are named by dimension ID (`minecraft:overworld`); on Paper they are Bukkit world names. Both are the names that platform already reports everywhere else.

### The switch is a switch, not a delete

`enabled: false` restores the global range everywhere and **leaves everything below it intact**, so an operator who turns overrides on for an event can turn them off again without retyping any of it. Both states apply through `/ssvc-server reload` without disconnecting anybody.

The block is also parsed and validated while it is off. A malformed entry is refused by the reload that introduced it — with the running settings left untouched, as with any rejected reload — rather than lying in wait until the switch is next flipped.

### A range of 0 means no voice, in both directions

Nobody in that place is heard outside it, **and nobody outside is heard in it**.

That follows from the rule for a speaker and a listener who resolve different ranges: **the smaller of the two applies to both**. Taking only the speaker's range would let somebody standing in a silenced booth listen in on the room outside; taking only the listener's would let them be heard from it. Either is a hole in the thing the operator set out to do.

<Callout title="Voice groups are not a place">
  A [voice group](/server/groups) already ignores distance, worlds and visibility, and it ignores range overrides for the same reason: it is a channel, not a location. Two people in the same group hear each other in a silenced world. If you need a place nobody can talk from at all, take `socketsvc.speak` away there, or switch groups off.
</Callout>

### Regions

A region is an axis-aligned box in one world. `min` and `max` are two opposite corners written as `[x, y, z]`, both inclusive, and it does not matter which corner is which.

A region is checked **before** the world it sits in, so a region can be quieter or louder than its surroundings. Where two regions overlap, the **first one listed** wins — file order is a tie-break an operator can see and reorder, which smallest-box-wins would not be.

Omitting `hearing-range` inherits it: a region from its world, a world from the global setting. So a region can narrow the fade alone. `fade-start-distance` inherits too, but only while it still fits — it is an absolute distance, so a global fade of 33 blocks means nothing inside a 16-block arena and falls back to 70% of that range instead, the same curve the global default uses.

<Callout type="warn" title="Regions are defined here, not read from WorldGuard">
  WorldGuard is Bukkit-only, and the Fabric/NeoForge/Forge server mods are promised the same features as the Paper plugin — a region system that worked on one of the four would break that. A box in the config behaves identically on all of them and needs no third-party API to follow. Reading real WorldGuard regions is a candidate for a separate addon, for servers that already draw their regions there.
</Callout>

### Checking it

`/ssvc-server status` lists every configured override and says whether they are being applied. `/ssvc-server test <player>` adds a line naming the world or region that player is standing in and the range in force there — "nobody can hear me" and "I am standing in a region with its own range" look identical from the outside, and that is the command that tells them apart.

<Callout title="Plugin speakers are unaffected">
  A speaker created through the [spatial audio API](/server/spatial-audio-api) carries the range its own `SpatialSettings` declares. An override changes where *players* are heard, and does not silently retune a plugin's speakers underneath it.
</Callout>

### Default config file

```yaml title="plugins/SocketSVC/config.yml"
port: 24455
bind-address: "0.0.0.0"
public-url: ""
public-host: localhost
hearing-range: 48.0
fade-start-distance: 0.0
spectator-interaction: false
visible-players-only: false
soundboard-enabled: true
server-list-advert: true

groups:
  enabled: false
  max-groups: 32
  max-members: 16
  allow-player-created: true
  allow-passwords: true
  allow-persistent: false
  default-type: normal

microphone-tap:
  enabled: false

metrics:
  enabled: true
```

The server mod's `config/socketsvc-server.yml` has the same content without the `metrics` block.

## Server list badge

With `server-list-advert` on (the default), a player running the SocketSVC client mod sees a microphone badge beside this server's row in their multiplayer server list, in the margin to the left of it — so they know voice chat works here without joining first.

It rides on the status ping the list already sends. The server appends an empty component to its MOTD carrying nothing but a style `insertion`, which is the one style field with no rendering of its own: it is the text a shift-click would put in the chat box, and the server list offers no such action. A vanilla client therefore renders exactly the MOTD it would have rendered, at the same width and with the same line wrapping. No extra connection, no extra port, no new protocol.

Turn it off by setting `server-list-advert: false` and running `/ssvc-server reload`; it applies to the next ping. Players can also hide the badge on their side under Advanced → Interface → **Server list voice badge**.

<Callout title="It can be lost, and that is fine">
  An MOTD plugin that rebuilds the description from a plain string — common with packet-library MOTD plugins — drops the sibling, so the badge does not appear. Nothing else changes. SocketSVC appends at the lowest-running event priority so a plugin that merely *sets* the MOTD keeps it.
</Callout>

On a Velocity or BungeeCord network the badge has to come from the relay plugin instead: players ping the proxy, and each backend's own voice listener is internal and never answers a server-list ping. A backend's `server-list-advert` does nothing there. See [Proxy configuration](/proxy/configuration#server-list-badge).

## Public URL

When `public-url` is blank, `ws://<public-host>:<port>` is built automatically, which suits LAN or direct connections without TLS. For a public server, set `public-url` to the `wss://` hostname of your reverse proxy or Cloudflare Tunnel (for example `wss://mydomain.com`); the proxy terminates TLS and forwards to the local `port`. See [Public deployment and WSS](/server#public-deployment-and-wss) for details.

With a Velocity/BungeeCord relay, `bind-address`/`port` only need to be reachable by the relay, and `public-host`/`public-url` must point at the relay's public address; see [Proxy configuration](/proxy/configuration).

## Visible players only

When `visible-players-only` is true, a player's voice only reaches listeners that are tracking the speaker's entity and allowed to see it. Microphone audio relayed through spatial speakers is filtered the same way. The state is refreshed every tick.

| Server side | How visibility is decided |
| --- | --- |
| Paper | Entity tracking updated by the server every tick, plus Bukkit `canSee` state (`hidePlayer`/`hideEntity`) |
| Server mod | There is no vanilla hidden-player API, so it checks same dimension and within the player entity tracking distance (`min(view-distance, player tracking range)`) |

- Neither is affected by camera direction, walls or client render culling.
- Hiding plugins that only intercept packets without updating Bukkit visibility/tracking state cannot be detected by this setting.
- Standalone audio sources (server-played PCM/WAV and so on) are unaffected.
- A missing or false value keeps the previous behaviour.

## Changing settings by command

`/ssvc-server settings` lists the everyday settings and their values. Click a name to start typing `/ssvc-server settings set <name> <value>`, which changes one: `on`/`off` for a switch, a number of blocks for a distance. Names and config keys both work.

| Name | Config key |
| --- | --- |
| `range` | `hearing-range` |
| `fade` | `fade-start-distance` |
| `spectators` | `spectator-interaction` |
| `visible-only` | `visible-players-only` |
| `soundboard` | `soundboard-enabled` |
| `groups` | `groups.enabled` |
| `whisper`, `whisper-range` | `voice-ranges.whisper.enabled`, `.hearing-range` |
| `shout`, `shout-range` | `voice-ranges.shout.enabled`, `.hearing-range` |
| `require-client` | `require-client.enabled` |
| `root-commands` | `addons.root-commands.enabled` |

- **Saving is a reload.** The value is written into the config file and `/ssvc-server reload` runs, so a change is applied exactly as a hand edit would be. The edited file is validated first: a value the reload would refuse is refused without the file being touched.
- **The file keeps its comments.** Only the line holding the value is rewritten; everything else in the file, comments included, stays as it was. A key missing from an older file is added under its section.
- **A fade at or past the hearing range is refused**, rather than being quietly ignored as it would be in the file.
- **What is not here.** The port, bind address and public URLs are left out - a slip there disconnects everybody - and so are per-world ranges and regions, messages, and every other text setting. Edit those in the file.
- It needs `socketsvc.admin`, like every `/ssvc-server` command.

## Hot reload

After editing the config, run `/ssvc-server reload` (the `/` can be omitted in the console). It requires the `socketsvc.admin` permission; see [Permissions](/server/permissions). This is SocketSVC's own config hot reload; Bukkit `/reload` is not needed.

- **Routing settings**: range, fade, spectator rules, `visible-players-only`, `soundboard-enabled` and the `groups` block apply immediately and clear stale routed audio that has not been sent yet; voice connections stay up. Turning `groups.enabled` off also disbands every live group, and the groups capability itself is only picked up by a client when it next reconnects.
- **Listener address**: a port/bind address change stops the old listener and starts a new one in the background, briefly interrupting voice without affecting the Minecraft connection. Existing speaker API objects, IDs and playback sources are kept, and playback time of audio sources keeps advancing.
- **External URL**: a `public-url`/`public-host` change closes existing voice connections so clients automatically fetch a new token and connect to the new URL. Adjust external proxies and firewalls separately.
- **Errors**: invalid YAML or network settings are rejected. If the new listener is not ready within five seconds, the failed listener is cleaned up and the old address is rebound, keeping the previous settings. If the old address cannot be restored either, the error is reported explicitly and the voice plugin is disabled; details are in the server log.
- **Concurrency and status**: only one reload is accepted at a time; `/ssvc-server status` shows the actual listener address and reload state. The running settings are only updated on success, a failed reload never overwrites the file on disk, and you can reload again after fixing it.

<Callout title="Speakers owned by other plugins">
  Speakers managed by other plugins through the [Spatial audio API](/server/spatial-audio-api) are still updated by their owner through the API; they are not part of this config file.
</Callout>

## Updates

SocketSVC checks whether a newer build exists and tells administrators; **it never updates the server by itself**. Installing is always `/ssvc-server update confirm`, gated on `socketsvc.admin.update` (which falls back to operators when no permission plugin is installed).

```yaml
updates:
  enabled: true        # check and report; never installs on its own
  channel: release     # release, dev, or a branch channel the update service offers
  endpoint: ''         # blank uses the deploy SocketSVC ships with
  notify-admins: true  # tell holders of socketsvc.admin.update when they join
```

| Command | What it does |
| --- | --- |
| `/ssvc-server update` | Checks the channel and prints what would be installed, how big it is and when it applies |
| `/ssvc-server update confirm` | Downloads, verifies the SHA-256 and stages it |
| `/ssvc-server update list` | Every build on the channel, newest first, marking the installed one |

The Paper plugin puts the verified jar in Bukkit's update folder under the running plugin's file name, so the server replaces it on the next start. The Fabric/NeoForge/Forge server mod stages it beside its config and swaps it in as the server shuts down, logging to `updates/apply.log`. Update settings are read at start; `/ssvc-server reload` does not re-read them.