# /vote: every vote site in one place

> When each vote site opens again for a player, votes cast, how far the next reward is, a reminder in chat and a party bar that shows only when it matters. New in 0.3.

Source: https://stygion.eu/docs/keystone/vote-command
Updated: 2026-10-25

New in 0.3. `/vote` used to print a list of links. Now it answers what a player actually wants to know: **where can I vote right now, how many votes have I got, and how far is the next reward.** Everything below is a setting in `votes.toml` (and in the editor's Settings and **Votes** screens); nothing is tied to one server or to one vote site.

## What a player sees

```
» Vote for this server:
   » vote now | Czech-Craft - https://czech-craft.eu/server/you/vote/
   » in 1h 12m | Minecraft-list.cz - https://...
   » in ~23h 5m | minecraft-mp.com - https://...
Every site on one page: https://example.com/vote
You can vote on 1 of 7 sites right now.
Your votes: 3 today, 41 in all, 4 days running.
Streak reward at 7 days in a row: 3 days to go.
This month: 11 votes - 4 more for a prize place (the top 3). /vote top
Vote party: 12 of 25 - 13 to go.
```

Every address is a link a player can click. A time with a `~` is an estimate; one without comes from the vote site itself.

## Telling the sites apart: `votes.lists`

One line per site, parts separated by ` | `:

```
service=Czech-Craft.eu | name=Czech-Craft | link=https://czech-craft.eu/server/you/vote/ | every=2h | check=https://czech-craft.eu/api/server/you/player/{player}/next_vote/ | path=next_vote | tz=Europe/Prague
service=Minecraft-MP.com | name=minecraft-mp.com | link=https://minecraft-mp.com/server/123/vote/ | every=24h
```

- `service` (required) is the name the site sends with its vote, exactly as in `votes.services`.
- `name` and `link` are what players read and click.
- `every` is how long that site makes a player wait.
- `check` and `path` are optional: an **https** address the site answers *when can this player vote next* on, and where the time sits in its JSON answer (`next_vote`, `[0].nextPossibleVote`, `data.next`). `{player}`, `{year}` and `{month}` are filled in. A time can be seconds or milliseconds since 1970, an ISO time, or `2026-10-24 13:34:51` in the zone `tz` (default: `votes.time-zone`).
- `{key:name}` in `check` takes a key from the file `lists.keys` in the votes data folder. Two lines per key: `name=the-key` and `name.host=api.example.com`. **A key is sent only to the host written beside it**: it is filled in only into an https address whose host is exactly that one (never into the host part itself), otherwise it is not used and the log says so. Keys are never written in the settings, so the editor, the API and the assistant never see them, and whoever can change the settings cannot send a key to a server of their own.

**Where the time comes from.** If the site answers, that answer is used. If it does not have such an address, is down, is slow (`votes.lists-timeout`, default 4 seconds) or answers something unreadable, Keystone estimates: **the last vote this server received from that site plus `every`** (or, without `every`, the interval `votes.site-cooldown` counts votes with). A site that failed is left alone for 45 seconds, so a dead site does not slow every player's `/vote`. No time is written in the code.

**What leaves the server, and where it may go.** Only when you write `check`: the player's name, in the address you wrote, to the site you named, when that player types `/vote` (answers are remembered for up to a minute; nothing is sent for the chat reminder). Leave `check` out and nothing is sent anywhere. Only https is used, and only to **public** addresses: a name that resolves to this machine, a private network, a link-local address (including the cloud metadata address), carrier-grade NAT or a unique-local IPv6 address is refused before anything is sent, so the settings cannot be used to reach what only this machine can. Redirects are not followed, an answer is read to 64 KiB at most, and what a site answered is never shown to anyone: the editor and the API only get the time (or that it was estimated).

With `votes.lists` empty, `/vote` prints the lines of `votes.sites` as before.

## The page

`votes.page` is one link to a page with every site on it, such as your website's vote page. `/vote` prints it under the sites.

## Votes, streaks and the month

- **Today / in all / streak** come from the same table the vote rules read, so the number a player sees is the number the rules use.
- **Streak reward**: the nearest `votes.streaks` line above the player's streak and how many days are left.
- **The month**: the player's place and votes, and with `votes.top.rewards` how many more votes reach the last prize place. `/vote top` shows the board.

## A reminder in chat

`votes.remind.chat` (on by default): to a player who can vote somewhere, *"You can vote on 5 of 7 sites right now"*, at most every `votes.remind.chat-every` (default 3 hours), the first one `votes.remind.chat-after-join` (default 3 minutes) after joining. It is checked every five minutes and uses only what the server already knows, so it sends no request to any site. A player switches it off with **`/vote remind chat off`** (and on with `/vote remind chat on`). `/vote remind on` is the other reminder, a private Discord message.

## The party bar

The boss bar that counts the last votes to the party goal used to stay on screen the whole time. Now it shows for `votes.party.bar-for` (default 20 seconds) after a vote moves it, and goes. `0s` keeps the old behaviour: on while the count is within `votes.party.bar-from` of the goal.

## In the editor and the API

The **Votes** screen lists the sites and, for a player you look up, when each opens for them. `GET /v1/votes/lists` (scope `players.read`) returns the same, with `?player=` for one player.

**Every vote, to your own vote page** (`votes.report.*`, off until set): `report.url` is an https address that is POSTed JSON - `{"type":"vote","server","player","list","at"}` for each accepted vote, and `{"type":"lists","server","lists":[…]}` every `report.every` (5 minutes, only when they changed) with the vote lists and their waits - for a full vote page on your website or a bot. `report.key` goes as a Bearer token, `report.server` names this server in the reports (a world key such as `mc`; empty sends none). Addresses inside the network are refused unless the owner sets `report.allow-local`; only the owner may set any of it.

Texts are in the language files: `votes.where.*`, `votes.list.*` and `votes.remind.chat*`; change them in the editor's Languages screen.
