Every REST endpoint and Socket.IO event the server answers
Two surfaces. REST handles uploads, downloads and a few things that want plain
HTTP caching. Everything interactive runs over Socket.IO: chat, presence, voice
signalling, moderation, settings.
There are no authentication endpoints here. Identity comes from an account
issuer or from a keypair on the device, and joining is a signed challenge over
the socket. See Identity.
identityTiers and joinPolicy are deliberately unauthenticated. The point is
that a client can say "you do not need an account for this one" before anybody
tries, and neither field tells a caller anything they could not learn by
attempting the join and reading the refusal.
Two things depend on who is asking. A server whose discoverable setting is off
answers 404 to anybody who is not already a member, so it disappears rather
than advertising itself. And version is only included for members, because a
precise build number lets anyone on the network scan for hosts running a version
with a known vulnerability, and this endpoint has to stay open for the add-server
flow.
No JWKS endpoint
The server does not publish /.well-known/jwks.json. It fetches one, from the
identity issuer it is configured to trust, and caches it. Nothing here serves
keys.
A separate listener on GRYT_ADMIN_PORT (5099), started only when
GRYT_ADMIN_TOKEN is set, published to loopback only. It exists so somebody
administering the machine can change database-backed settings without being the
server's owner.
Method
Path
Returns
GET
/management/settings
joinPolicy, discoverable, lanOpen, profanityMode
PATCH
/management/settings
The same, updated. Send only the keys you are changing
GET
/management/health
{ ok: true }
Authorization: Bearer <GRYT_ADMIN_TOKEN> on all three. Requires server 1.5.0.
A PATCH goes through the same apply path as a change made from a client, so it
withdraws mDNS advertising, drops caches and writes an audit entry rather than
only updating a row.
The handshake is three messages, and it is a signature exchange rather than a
password.
Event
Direction
Payload
server:info
client → server
—
server:join
client → server
{ nickname?, inviteCode? }
server:challenge
server → client
A nonce to sign
server:verify
client → server
{ certificate, assertion }
server:joined
server → client
Success, with tokens
server:setup_required
server → client
Nothing has claimed ownership yet
The server also proves itself, over server:identify, as a connection-level
exchange rather than part of the join. A client reconnecting with a saved token
never joins at all, so a proof carried on join would leave the common path
handing a bearer token to a server nobody authenticated.
Refusals come back on server:error with a reason: invite_required,
invalid_invite, invite_rate_limited, identity_tier_refused,
identity_verification_failed, challenge_expired, nonce_mismatch,
account_already_member, approval_pending, auth_required, auth_disabled,
auth_misconfigured, join_refused, join_failed, invalid_nickname.