A first-party Rust clan is a Team (R5). This module becomes the site's Team provider and answers core from the plugin's `clans` board. Design of record: docs/modules/rust/PLAN.md §24, D47-D58. - The store: rust_clans, rust_clan_members and rust_clan_boards. A clan's identity is <serverId>:<clanId>:<createdMs> (D52), because the game restarts clan ids whenever its clan database version changes. - The provider (D53): getTeams is complete only when every server's board is fresh, supported and untruncated. It is partial when some are, and refuses when none are. Freshness is judged by the website's clock, from when the board's `t` last advanced. - Only a complete board may mark a clan gone. A board at the game's 100-clan ceiling (D55), or one with an unreadable row, proves nothing about what it leaves out. - Leadership is diffed board to board and published (D54). The five clan events are published as team.* kinds, and written to the Team feed as members-only lines (D49). - Core only writes feed items for a Team it already holds. So the last 10 minutes of clan events are re-offered on each board refresh, deduped by a sha1 key: core clamps a dedupeKey to 40 characters, and a readable key would be truncated into collisions. - projectRoster and the clan page share one audience rule (D48): the clan's linked members and staff by default, re-read from the users row. The setting lives on Admin > Rust visibility, which also warns about uMod Clans (D47) and the ceiling. - Public: GET servers/:id/clans (the list is public, D58) and GET clans/:externalId. The client adds a Clans tab and /rust/clans/:externalId, with three module slots for core's notify, activity and forum contributions (D56). - Linking and unlinking an account ask core to reconcile Teams (D57). - The clan kinds are staff-class in the public feed allowlist. - PROTOCOL_VERSION is now 6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
685 lines
30 KiB
JavaScript
685 lines
30 KiB
JavaScript
// ── The OpenAPI fragment: the shared half ─────────────────────────────────
|
||
//
|
||
// The tags and component schemas the `#swagger.*` annotations refer to.
|
||
// `scripts/swaggerFragment.js` feeds this to swagger-autogen; the per-endpoint
|
||
// detail lives beside each route, exactly as it does in core.
|
||
//
|
||
// **Two rules about names, and both belong to the MERGED document rather than to
|
||
// this file** (MODULE_API.md §6.1a). Core merges every started module's fragment
|
||
// over its own committed spec and serves the result at `/api/docs.json`, and core
|
||
// wins any key collision:
|
||
//
|
||
// • **Namespace what you DEFINE.** `RustServerList`, not `ServerList`. A second
|
||
// game's module describing the same idea under the same bare name would
|
||
// silently clobber this one or be clobbered by it.
|
||
// • **Reference what CORE defines by core's name.** `#/components/schemas/Error`
|
||
// and `ValidationError` are core's; point at them and do not redefine them.
|
||
//
|
||
// **swagger-autogen renders `components.schemas` from an EXAMPLE object, not from
|
||
// raw OpenAPI.** `{ type: 'object' }` comes back as a meta-description of itself.
|
||
// That is uniform across core's committed spec and is the house shape.
|
||
|
||
module.exports = {
|
||
tags: [
|
||
{
|
||
name: 'Public · Rust',
|
||
description: 'The Rust servers this site follows, as each one last reported itself',
|
||
},
|
||
{
|
||
name: 'Player · Rust',
|
||
description: 'The Rust surface for a signed-in player',
|
||
},
|
||
{
|
||
name: 'Admin · Rust',
|
||
description: 'Configuring the Rust servers and their sidecars',
|
||
},
|
||
],
|
||
components: {
|
||
schemas: {
|
||
RustServerList: {
|
||
type: 'object',
|
||
description: 'Every Rust server this site follows (GET /public/rust/servers).',
|
||
properties: {
|
||
servers: {
|
||
type: 'array',
|
||
items: { $ref: '#/components/schemas/RustServer' },
|
||
},
|
||
},
|
||
},
|
||
RustServer: {
|
||
type: 'object',
|
||
description: 'One Rust server, as it last reported itself.',
|
||
properties: {
|
||
id: { type: 'string', example: 'main' },
|
||
name: { type: 'string', example: 'Main · Vanilla' },
|
||
online: { type: 'boolean', example: true },
|
||
players: { type: 'integer', example: 42 },
|
||
maxPlayers: { type: 'integer', example: 100 },
|
||
hostname: { type: 'string', nullable: true, example: 'Runic Gateway · Main' },
|
||
level: { type: 'string', nullable: true, example: 'Procedural Map' },
|
||
worldSize: { type: 'integer', nullable: true, example: 4000 },
|
||
seed: { type: 'integer', nullable: true, example: 1234 },
|
||
updatedAt: { type: 'string', format: 'date-time', nullable: true },
|
||
stale: {
|
||
type: 'boolean',
|
||
description: 'Has nothing reported in longer than the freshness window? A stale row is reported offline.',
|
||
example: false,
|
||
},
|
||
},
|
||
},
|
||
RustAdminServerList: {
|
||
type: 'object',
|
||
description: 'The configured servers, with their sidecar settings (GET /admin/rust/servers).',
|
||
properties: {
|
||
servers: {
|
||
type: 'array',
|
||
items: { $ref: '#/components/schemas/RustAdminServer' },
|
||
},
|
||
},
|
||
},
|
||
RustAdminServer: {
|
||
type: 'object',
|
||
description: 'One configured server. The sidecar token is never included — `hasToken` reports only whether one is stored.',
|
||
properties: {
|
||
id: { type: 'string', example: 'main' },
|
||
name: { type: 'string', example: 'Main · Vanilla' },
|
||
sidecarBaseUrl: { type: 'string', example: 'http://10.0.0.5:8090' },
|
||
hasToken: { type: 'boolean', example: true },
|
||
protocol: { type: 'integer', example: 1 },
|
||
enabled: { type: 'boolean', example: true },
|
||
sortOrder: { type: 'integer', example: 0 },
|
||
reachable: {
|
||
type: 'boolean',
|
||
description: 'Did the sidecar answer on the last poll? Separate from `online`, which is about the game rather than the bridge.',
|
||
example: true,
|
||
},
|
||
bootId: { type: 'string', nullable: true, example: 'boot-20260915T194502Z' },
|
||
sidecarProtocol: { type: 'integer', nullable: true, example: 1 },
|
||
online: { type: 'boolean', example: true },
|
||
players: { type: 'integer', example: 42 },
|
||
stale: { type: 'boolean', example: false },
|
||
},
|
||
},
|
||
RustLink: {
|
||
type: 'object',
|
||
description: 'One Steam account linked to a website user. Never carries a code.',
|
||
properties: {
|
||
steamId: { type: 'string', example: '76561198000000000' },
|
||
name: {
|
||
type: 'string',
|
||
nullable: true,
|
||
description: 'What the player was called in game when they linked. A display name only — a Rust name changes on a whim and nothing identifies anybody by it.',
|
||
example: 'Wanderer',
|
||
},
|
||
serverId: {
|
||
type: 'string',
|
||
nullable: true,
|
||
description: 'Which server minted the code. Not part of the identity — a link is fleet-wide — but it is where a support conversation starts.',
|
||
example: 'main',
|
||
},
|
||
linkedAt: { type: 'string', format: 'date-time' },
|
||
},
|
||
},
|
||
RustLinkList: {
|
||
type: 'object',
|
||
description: 'The Steam accounts one website user holds (GET /player/rust/links).',
|
||
properties: {
|
||
links: { type: 'array', items: { $ref: '#/components/schemas/RustLink' } },
|
||
},
|
||
},
|
||
RustPlayerReach: {
|
||
type: 'object',
|
||
description: 'One server an entitlement’s scope reaches, and whether it is there yet.',
|
||
properties: {
|
||
id: { type: 'string', example: 'main' },
|
||
name: { type: 'string', example: 'Main · Vanilla+' },
|
||
live: {
|
||
type: 'boolean',
|
||
description: 'True only when a sync confirmed this into that server’s own store. False covers every way it has not arrived — the server is offline, no loaded plugin registered the name, or its store has never seen the account — and the difference between those is an operator’s diagnosis, not a player’s.',
|
||
example: true,
|
||
},
|
||
},
|
||
},
|
||
RustPlayerPermissions: {
|
||
type: 'object',
|
||
description: 'What the site has given the signed-in player in game (GET /player/rust/permissions).',
|
||
properties: {
|
||
accounts: {
|
||
type: 'integer',
|
||
description: 'How many Steam accounts the caller has linked. Zero is why an entitlement can be authored and reach nobody.',
|
||
example: 1,
|
||
},
|
||
groups: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
name: { type: 'string', example: 'vip' },
|
||
title: { type: 'string', example: 'VIP' },
|
||
scope: { type: 'string', description: 'A server id, or `*` for the whole fleet.', example: '*' },
|
||
since: { type: 'string', format: 'date-time' },
|
||
permissions: { type: 'array', items: { type: 'string' }, example: ['kits.vip'] },
|
||
reach: { type: 'array', items: { $ref: '#/components/schemas/RustPlayerReach' } },
|
||
},
|
||
},
|
||
},
|
||
grants: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
permission: { type: 'string', example: 'kits.vip' },
|
||
scope: { type: 'string', example: '*' },
|
||
source: { type: 'string', description: 'Who authored it — `admin` now, an event action later.', example: 'admin' },
|
||
note: { type: 'string', nullable: true },
|
||
since: { type: 'string', format: 'date-time' },
|
||
reach: { type: 'array', items: { $ref: '#/components/schemas/RustPlayerReach' } },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
RustLinkRequest: {
|
||
type: 'object',
|
||
required: ['code'],
|
||
properties: {
|
||
code: {
|
||
type: 'string',
|
||
description: 'The six-character code /link handed the player in game. Good for five minutes, and it works once.',
|
||
example: 'K7M2PQ',
|
||
},
|
||
},
|
||
},
|
||
RustLinkResult: {
|
||
type: 'object',
|
||
description: 'The result of redeeming a code.',
|
||
properties: {
|
||
linked: { type: 'boolean', example: true },
|
||
link: { $ref: '#/components/schemas/RustLink' },
|
||
already: {
|
||
type: 'boolean',
|
||
description: 'True when this Steam id was already linked to the caller — a second press of the button, not an error.',
|
||
example: false,
|
||
},
|
||
},
|
||
},
|
||
RustAdminLinkList: {
|
||
type: 'object',
|
||
description: 'One user’s Rust identity, for the admin.users.detail panel (GET /admin/users/{id}/rust/links).',
|
||
properties: {
|
||
links: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
steamId: { type: 'string', example: '76561198000000000' },
|
||
name: {
|
||
type: 'string',
|
||
nullable: true,
|
||
description: 'What the game last saw this player called, falling back to the name recorded at link time.',
|
||
example: 'Wanderer',
|
||
},
|
||
linkedName: { type: 'string', nullable: true, example: 'Wanderer' },
|
||
serverId: { type: 'string', nullable: true, example: 'main' },
|
||
linkedAt: { type: 'string', format: 'date-time' },
|
||
firstSeen: { type: 'string', format: 'date-time', nullable: true },
|
||
lastSeen: { type: 'string', format: 'date-time', nullable: true },
|
||
servers: {
|
||
type: 'array',
|
||
description: 'All-time totals per server, summed across every wipe.',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
serverId: { type: 'string', example: 'main' },
|
||
serverName: { type: 'string', example: 'Main · Vanilla' },
|
||
kills: { type: 'integer', example: 41 },
|
||
deaths: { type: 'integer', example: 37 },
|
||
npcKills: { type: 'integer', example: 120 },
|
||
structures: { type: 'integer', example: 64 },
|
||
playtimeSec: { type: 'integer', example: 43200 },
|
||
wipes: { type: 'integer', example: 2 },
|
||
lastSeen: { type: 'string', format: 'date-time', nullable: true },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
RustPermissionModel: {
|
||
type: 'object',
|
||
description:
|
||
'The whole permission model (GET /admin/rust/permissions): what the site authors, what each game reported back, and the names a grant may use.',
|
||
properties: {
|
||
groups: {
|
||
type: 'array',
|
||
description: 'Groups the site authors, mirrored into each in-scope game as a real group.',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
name: { type: 'string', example: 'vip' },
|
||
title: { type: 'string', example: 'VIP' },
|
||
rank: { type: 'integer', example: 10 },
|
||
scope: {
|
||
type: 'string',
|
||
description: 'A server id, or `*` for every server.',
|
||
example: '*',
|
||
},
|
||
permissions: { type: 'array', items: { type: 'string', example: 'kits.vip' } },
|
||
members: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
userId: { type: 'integer', example: 42 },
|
||
username: { type: 'string', example: 'wanderer' },
|
||
steamId: {
|
||
type: 'string',
|
||
nullable: true,
|
||
description: 'Null when this account has linked no Steam id, in which case the membership reaches nobody yet.',
|
||
example: '76561198000000000',
|
||
},
|
||
playerName: { type: 'string', nullable: true, example: 'Wanderer' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
grants: {
|
||
type: 'array',
|
||
description: 'Permissions held by one person without a group. Unlike membership, a direct grant reaches a player who has never connected.',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
id: { type: 'integer', example: 7 },
|
||
userId: { type: 'integer', example: 42 },
|
||
username: { type: 'string', example: 'wanderer' },
|
||
permission: { type: 'string', example: 'kits.gold' },
|
||
scope: { type: 'string', example: 'main' },
|
||
source: {
|
||
type: 'string',
|
||
description: 'What authored it — `admin`, `adopted`, or a later phase’s own writer.',
|
||
example: 'admin',
|
||
},
|
||
note: { type: 'string', nullable: true, example: null },
|
||
grantedAt: { type: 'string', format: 'date-time' },
|
||
accounts: {
|
||
type: 'array',
|
||
description: 'The Steam accounts this grant reaches. Empty means it reaches nobody yet.',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
steamId: { type: 'string', example: '76561198000000000' },
|
||
name: { type: 'string', nullable: true, example: 'Wanderer' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
servers: {
|
||
type: 'array',
|
||
description: 'The state of the mirror, per configured server.',
|
||
items: { $ref: '#/components/schemas/RustPermissionSyncState' },
|
||
},
|
||
drift: {
|
||
type: 'array',
|
||
description: 'What a game holds that the site did not author. Reported, never undone.',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
id: { type: 'integer', example: 3 },
|
||
serverId: { type: 'string', example: 'main' },
|
||
kind: {
|
||
type: 'string',
|
||
description: 'One of `grant`, `member`, `group-permission`.',
|
||
example: 'grant',
|
||
},
|
||
subject: {
|
||
type: 'string',
|
||
description: 'A Steam id, or a group name.',
|
||
example: '76561198000000000',
|
||
},
|
||
object: {
|
||
type: 'string',
|
||
description: 'A permission name, or a group name.',
|
||
example: 'kits.admin',
|
||
},
|
||
username: {
|
||
type: 'string',
|
||
nullable: true,
|
||
description: 'The website account holding that Steam id, when there is one. Without it the drift cannot be adopted, only revoked.',
|
||
example: 'wanderer',
|
||
},
|
||
firstSeen: { type: 'string', format: 'date-time' },
|
||
},
|
||
},
|
||
},
|
||
catalogue: {
|
||
type: 'array',
|
||
items: { $ref: '#/components/schemas/RustPermissionCatalogueEntry' },
|
||
},
|
||
},
|
||
},
|
||
RustPermissionSyncState: {
|
||
type: 'object',
|
||
description: 'Whether one server’s store matches what the site authors, and what its last report said.',
|
||
properties: {
|
||
serverId: { type: 'string', example: 'main' },
|
||
state: {
|
||
type: 'string',
|
||
description: 'One of `pending`, `ok`, `failed`.',
|
||
example: 'ok',
|
||
},
|
||
inSync: {
|
||
type: 'boolean',
|
||
description: 'True when the last successful push carried the set the site currently authors.',
|
||
example: true,
|
||
},
|
||
dirty: { type: 'boolean', example: false },
|
||
lastAttemptAt: { type: 'string', format: 'date-time', nullable: true },
|
||
lastOkAt: { type: 'string', format: 'date-time', nullable: true },
|
||
error: {
|
||
type: 'string',
|
||
nullable: true,
|
||
description: 'Why the last attempt failed — a transport word (`timeout`, `no-token`, `protocol-mismatch`) or the game’s own refusal.',
|
||
example: null,
|
||
},
|
||
report: {
|
||
type: 'object',
|
||
nullable: true,
|
||
description: 'The plugin’s report from the last successful sync.',
|
||
properties: {
|
||
applied: {
|
||
type: 'object',
|
||
properties: {
|
||
grants: { type: 'integer', example: 2 },
|
||
revokes: { type: 'integer', example: 0 },
|
||
groupsCreated: { type: 'integer', example: 1 },
|
||
members: { type: 'integer', example: 3 },
|
||
},
|
||
},
|
||
alreadyCorrect: { type: 'integer', example: 14 },
|
||
unresolved: {
|
||
type: 'array',
|
||
description: 'Permission names no loaded plugin on that server has registered. A grant naming one lands nowhere and is not recorded as pushed.',
|
||
items: { type: 'string', example: 'kits.gold' },
|
||
},
|
||
pending: {
|
||
type: 'array',
|
||
description: 'Memberships waiting on a first connection: the store has no user record to put in a group yet.',
|
||
items: { type: 'string', example: '76561198000000000:vip' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
RustPermissionCatalogue: {
|
||
type: 'object',
|
||
description: 'Every permission name the configured servers have registered (GET /admin/rust/permissions/catalogue).',
|
||
properties: {
|
||
permissions: {
|
||
type: 'array',
|
||
items: { $ref: '#/components/schemas/RustPermissionCatalogueEntry' },
|
||
},
|
||
},
|
||
},
|
||
RustPermissionCatalogueEntry: {
|
||
type: 'object',
|
||
description: 'One registered permission name, and which servers know it.',
|
||
properties: {
|
||
permission: { type: 'string', example: 'kits.vip' },
|
||
servers: { type: 'array', items: { type: 'string', example: 'main' } },
|
||
},
|
||
},
|
||
RustPermissionSyncResult: {
|
||
type: 'object',
|
||
description: 'What a forced sync produced (POST /admin/rust/permissions/sync).',
|
||
properties: {
|
||
servers: { type: 'array', items: { $ref: '#/components/schemas/RustPermissionSyncState' } },
|
||
drift: { type: 'array', items: { type: 'object' } },
|
||
},
|
||
},
|
||
RustUserPermissions: {
|
||
type: 'object',
|
||
description: 'One person’s Rust privileges, for the admin.users.detail panel (GET /admin/users/{id}/rust/permissions).',
|
||
properties: {
|
||
groups: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
name: { type: 'string', example: 'vip' },
|
||
title: { type: 'string', example: 'VIP' },
|
||
scope: { type: 'string', example: '*' },
|
||
permissions: { type: 'array', items: { type: 'string', example: 'kits.vip' } },
|
||
},
|
||
},
|
||
},
|
||
grants: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
id: { type: 'integer', example: 7 },
|
||
permission: { type: 'string', example: 'kits.gold' },
|
||
scope: { type: 'string', example: 'main' },
|
||
source: { type: 'string', example: 'admin' },
|
||
grantedAt: { type: 'string', format: 'date-time' },
|
||
},
|
||
},
|
||
},
|
||
reaches: {
|
||
type: 'array',
|
||
description: 'The Steam accounts these privileges reach. Empty means this person has linked nothing and holds them on paper only.',
|
||
items: { type: 'string', example: '76561198000000000' },
|
||
},
|
||
},
|
||
},
|
||
RustOnline: {
|
||
type: 'object',
|
||
description: 'Who is on one server (GET /public/rust/servers/{id}/online). Below the operator’s presence audience the names are withheld and only the count is answered — nothing names who is online by default.',
|
||
properties: {
|
||
players: {
|
||
type: 'array',
|
||
description: 'Empty whenever `hidden` is true.',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
steamId: { type: 'string', example: '76561198000000000' },
|
||
name: { type: 'string', nullable: true, example: 'Wanderer' },
|
||
sleeping: { type: 'boolean', example: false },
|
||
connectedAt: { type: 'string', nullable: true },
|
||
},
|
||
},
|
||
},
|
||
hidden: { type: 'boolean', description: 'Were the names withheld from this viewer?', example: true },
|
||
count: { type: 'integer', description: 'How many are online. Public at every audience.', example: 12 },
|
||
audience: { $ref: '#/components/schemas/RustAudience' },
|
||
},
|
||
},
|
||
RustAudience: {
|
||
type: 'string',
|
||
enum: ['staff', 'signed_in', 'public'],
|
||
description: 'Who may see something: admins and moderators, any signed-in account, or anybody. Ordered — each includes the ones before it.',
|
||
example: 'staff',
|
||
},
|
||
RustVisibility: {
|
||
type: 'object',
|
||
description: 'Who may see who is online: the fleet default and each server’s optional override (GET /admin/rust/visibility).',
|
||
properties: {
|
||
audiences: { type: 'array', items: { $ref: '#/components/schemas/RustAudience' } },
|
||
presence: {
|
||
type: 'object',
|
||
properties: {
|
||
fleet: { $ref: '#/components/schemas/RustAudience' },
|
||
servers: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
id: { type: 'string', example: 'main' },
|
||
name: { type: 'string', example: 'Main · Vanilla' },
|
||
enabled: { type: 'boolean', example: true },
|
||
override: {
|
||
type: 'string',
|
||
nullable: true,
|
||
enum: ['staff', 'signed_in', 'public', null],
|
||
description: 'This server’s own choice, or null to follow the fleet default.',
|
||
},
|
||
effective: { $ref: '#/components/schemas/RustAudience' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
clans: {
|
||
type: 'object',
|
||
description: 'Who may see a clan roster, and each server’s clan board.',
|
||
properties: {
|
||
audiences: { type: 'array', items: { $ref: '#/components/schemas/RustClanAudience' } },
|
||
roster: { $ref: '#/components/schemas/RustClanAudience' },
|
||
servers: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
id: { type: 'string', example: 'main' },
|
||
name: { type: 'string', example: 'Main · Vanilla' },
|
||
supported: { type: 'boolean', example: true },
|
||
enabled: { type: 'boolean', example: true },
|
||
fresh: { type: 'boolean', example: true },
|
||
truncated: { type: 'boolean', example: false },
|
||
reason: { type: 'string', nullable: true },
|
||
clans: { type: 'integer', example: 14 },
|
||
umodClans: { type: 'boolean', description: 'Is the uMod Clans plugin loaded? Its clans are a separate system and are not Teams.', example: false },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
RustClanAudience: {
|
||
type: 'string',
|
||
enum: ['members', 'signed_in', 'public'],
|
||
description: 'Who may see a clan’s roster: the clan’s own members (a website account linked to one of them) and staff, any signed-in account, or anybody. Widening it also shows which members are online to that audience.',
|
||
example: 'members',
|
||
},
|
||
RustClanBoard: {
|
||
type: 'object',
|
||
description: 'Whether a server’s clan list can be trusted right now.',
|
||
properties: {
|
||
supported: { type: 'boolean', description: 'Could the bridge read this server’s clans at all?', example: true },
|
||
enabled: { type: 'boolean', description: 'Is the game’s clan system switched on?', example: true },
|
||
fresh: { type: 'boolean', description: 'Has the board been re-sent within the last three minutes?', example: true },
|
||
truncated: { type: 'boolean', description: 'At the game’s 100-clan ceiling, or too large for one line: there may be clans the list does not show.', example: false },
|
||
reason: { type: 'string', nullable: true, description: 'Why the clans cannot be read, when they cannot.' },
|
||
},
|
||
},
|
||
RustClanList: {
|
||
type: 'object',
|
||
description: 'One server’s clans (GET /public/rust/servers/{id}/clans). Public: nothing here names a player.',
|
||
properties: {
|
||
clans: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
externalId: { type: 'string', example: 'main:12:1790142840535' },
|
||
name: { type: 'string', example: 'Northwatch' },
|
||
color: { type: 'string', nullable: true, example: '#3fa9f5' },
|
||
score: { type: 'integer', example: 140 },
|
||
memberCount: { type: 'integer', example: 6 },
|
||
maxMembers: { type: 'integer', nullable: true, example: 100 },
|
||
},
|
||
},
|
||
},
|
||
board: { $ref: '#/components/schemas/RustClanBoard' },
|
||
},
|
||
},
|
||
RustClan: {
|
||
type: 'object',
|
||
description: 'One clan (GET /public/rust/clans/{externalId}) and, inside the roster audience, its roster.',
|
||
properties: {
|
||
clan: {
|
||
type: 'object',
|
||
properties: {
|
||
externalId: { type: 'string', example: 'main:12:1790142840535' },
|
||
name: { type: 'string', example: 'Northwatch' },
|
||
color: { type: 'string', nullable: true, example: '#3fa9f5' },
|
||
score: { type: 'integer', example: 140 },
|
||
memberCount: { type: 'integer', example: 6 },
|
||
maxMembers: { type: 'integer', nullable: true, example: 100 },
|
||
serverId: { type: 'string', example: 'main' },
|
||
serverName: { type: 'string', example: 'Main · Vanilla' },
|
||
founded: { type: 'integer', nullable: true, description: 'When the clan was founded, epoch milliseconds.' },
|
||
gone: { type: 'boolean', description: 'The clan has been disbanded, or has left its server’s board.', example: false },
|
||
},
|
||
},
|
||
roster: {
|
||
type: 'object',
|
||
properties: {
|
||
visible: { type: 'boolean', description: 'Is this viewer inside the roster audience? When false, `members` is empty.', example: false },
|
||
audience: { $ref: '#/components/schemas/RustClanAudience' },
|
||
members: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
name: { type: 'string', nullable: true, example: 'Wanderer' },
|
||
role: { type: 'string', nullable: true, example: 'Leader' },
|
||
leader: { type: 'boolean', example: true },
|
||
online: { type: 'boolean', example: false },
|
||
joined: { type: 'integer', nullable: true, description: 'Epoch milliseconds.' },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
RustVisibilityUpdate: {
|
||
type: 'object',
|
||
description: 'A change to who may see who is online. Either part may be omitted; a server set to null follows the fleet default again.',
|
||
properties: {
|
||
fleet: { $ref: '#/components/schemas/RustAudience' },
|
||
servers: {
|
||
type: 'object',
|
||
additionalProperties: { type: 'string', nullable: true, enum: ['staff', 'signed_in', 'public', null] },
|
||
example: { main: 'public', pvp: null },
|
||
},
|
||
clanRoster: { $ref: '#/components/schemas/RustClanAudience' },
|
||
},
|
||
},
|
||
RustSidecarProbe: {
|
||
type: 'object',
|
||
description: 'What a sidecar said when probed (POST /admin/rust/servers/{id}/test).',
|
||
properties: {
|
||
ok: { type: 'boolean', example: true },
|
||
status: {
|
||
type: 'string',
|
||
description: 'What happened, in one word — this is what tells a wrong URL from a wrong token from a mismatched protocol. One of `ok`, `no-token`, `unauthorized`, `protocol-mismatch`, `timeout`, `transport-error`, or `http-<code>`.',
|
||
example: 'ok',
|
||
},
|
||
sidecar: {
|
||
type: 'object',
|
||
nullable: true,
|
||
description: 'The sidecar’s own health document, or the mismatch detail on a protocol disagreement.',
|
||
properties: {
|
||
status: { type: 'string', example: 'ok' },
|
||
protocol: { type: 'integer', example: 1 },
|
||
plugin_connected: { type: 'boolean', example: true },
|
||
database: { type: 'string', example: 'ok' },
|
||
uptime: { type: 'string', example: '3h 2m' },
|
||
last_event: { type: 'string', format: 'date-time', nullable: true },
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|