Beta
STYGION Keystone b0.1.2: co je nového
Keystone's commands survive a /reload. Reloading data packs gives the game a
new command tree, and Keystone's commands were not in it until the next restart;
they are put back as soon as the reload lands, and every player is sent the new
tree.
Security, from the review of 8 October 2026.
- Votes are worth one vote. One vote per site per person per voting day, and
no more in a day than
/votelists sites (votes.limit.per-day, 0 = that many); the whole port takes at mostvotes.limit.per-minute(30) a minute. A vote counts as when it arrived here, never as the sender dated it, and one dated more than a day from this server's clock is refused — the original protocol is encrypted with a key every vote site holds, so its dates and site names are whatever the sender liked. A streak reward is paid once on its day, not once per vote that day.votes.accept-v1 = falsetakes only signed v2 votes. A sender that trickles bytes is cut off after ten seconds, and no more than 32 connections are open at once. - What can make somebody an operator is the owner's. Settings, groups, perks,
restoring a backup, running operations and the assistant's writes need a key
issued to the console or a level-4 operator — checked on every request, whatever
wildcard a key holds. A key from before owners were recorded is not the
owner's until the owner says so with
/keystone key own <id>; the log names each one at boot. A key somebody named like one of the mod's own is never widened into it. Applying an assistant's proposal in game, and revoking the owner's key, are the owner's too. A panel link for somebody who reached it through a group is read-and-moderate only./keystone key newhands out nothing its maker could not be handed. Vote rewards, streaks, party rewards and crate loot (which run as the console) can be changed in game only by the owner. "Owner" means operator level 4, as everywhere else, not any operator level. /execute aschanges who a command is about, never who is asking. Permissions and "the owner" come from whoever really typed the command: their own level and their own groups, never those of the playerexecute asnamed (somebody at op 2 could run Keystone's commands as the owner and be the owner). A command block or a function is level 2 with no groups, whoever it runs as, and is never the owner; the console and RCON are. A level-4 operator running a command as somebody else may use it but is not the owner for it. Logs name both:Alice as Bob.- A vote pays a person, not a name. On a server in offline mode a name costs
nothing, so a vote is always written down but pays only somebody who has played
here for
votes.pay.played(30 minutes) — a name nobody has joined as is never paid — and, withvotes.pay.discord, whose account is linked to Discord. Linked accounts count as their Discord account: several names on one Discord share one voting day and one streak reward a day. Waiting votes pay as soon as the conditions are met, and the player is told on joining what is missing. The per-minute limit is per vote site and counts only votes that pass the limits, so one noisy sender no longer shuts out the honest ones. - A linking code is no longer handed to whoever types a name that has played
here. With
link.required-to-joinon an offline-mode server, the door used to give a code to anybody under any unlinked name — and spending it on Discord made them that player, with the save, the rank and the statistics behind it, whatever password the real owner had. Now a name that has a world save or statistics, a rank, a row in Keystone's records or a password here gets no code at the door. With a password (andaccounts.requireon) they are let in, held, and leave with their code on the screen once/loginproves it; without one they readdiscord.screens.name-has-historyand open a ticket, and/discord permit <name>(keystone.discord.permit) lets that name get one code — used up by the link it makes, lapsing after a day. A name nobody has worn here gets its code as before; online-mode servers are unchanged. The permit is for the name exactly as written — capitals count, because on an offline-mode serverSteveandsteveare two people with two saves. - A name linked to Discord can no longer be taken with
/register. Withaccounts.requireandask-on-discordon, somebody joining under a linked name that had no password was held while its owner was asked on Discord — and could type/register x xand walk in without any answer. The same happened when no question could be sent (one already waiting, the bot down): they were offered/register. Now a linked name sets a password only after confirming on Discord, and when it cannot be asked it is disconnected. New settingaccounts.passwords(on by default): off removes/register,/loginand/passwordaltogether, so on a server where everybody links Discord the only proof is confirming the join there. /keystone key listmarks the owner's keys and says when each was made.- The local API reads nobody before it knows them, and at most a megabyte from anybody; MCP tool calls go through the same rate limit, owner check and audit record as every other request.
The editor, redrawn — and it shows the whole server now. /keystone panel
opens a new editor in STYGION's own colours, light or dark with the system (or
pinned by a switch), with everything grouped in a rail: an overview (players,
TPS, tick and memory now and over the last day, what is coming up, and what needs
somebody — open tickets, the assistant's proposals, a staged restore, a module
that did not start), players (look anybody up; what they hold and where from;
put them in a group for a while, give a perk, ask whether they may do something;
progress, votes and crate keys; leaderboards and the rank ladder), groups
with every rule they end up with and where it came from, bans and accounts,
operations (run now or in 1, 5 or 15 minutes, call off, maintenance with a
reason, what a clean would take), backups (and putting one back, staged for
the next boot and cancellable), performance (live, a week of history, load
tests), console and chat (levels, search, copy), votes, tickets,
crates, Discord, settings, the change history, the assistant
and keys and the audit log. Ctrl K (or /) finds any screen, setting,
player or group and runs the common actions. A screen only appears when its
module runs.
Settings are drafts until you apply them. Change as many as you like; each
is checked by the server as you type and refused in words if it does not fit.
Ctrl S shows every change beside what it is now, and applies the ones the
server took — each a version the history can put back, with an Undo right there.
Drafts survive a closed tab (secrets never leave the page). Flags are switches,
lists are chips, times of day are a clock, ids are picked from what the server
loaded, and chat text is drawn the way a vanilla client draws it.
API. POST /v1/settings takes check=true to ask whether a value would be
accepted without writing it. GET /v1/settings and the settings history answer
a list as a, b rather than [a, b], which could not be written back. GET /v1/backups answered 500 whenever no restore was staged — the usual case — and
now answers; so did GET /v1/gate whenever anybody had been let in by name.
The tick time in /v1/metrics is what a tick costs now, from the server's
own tick times — it was 1000 ÷ TPS, the gap between ticks, so every healthy
server read 50 ms "of the 50 a tick allows".
Crates: a key for every day you play. A new module, crates, off until
an owner turns it on (enabled = true in config/keystone/crates.toml) — a
module that hands things out is not something an update should switch on by
itself, so a module can now say it starts off (ModuleProvider.onByDefault).
Everybody gets one key on each day they are on the server — on their first join
of the day, or at midnight if they are online when it passes — counted in the
server's time zone (crates.time-zone). Keys live in Keystone's database, not
in an inventory, so they cannot be traded, dropped or duplicated; they are
earned, never sold. /crate shows how many you hold and your odds, /crate open spends one, /crate odds prints every rank's odds and what each rarity
holds. Admins have /keystone crate give <player> <n> (works for somebody
offline, and from a vote: keystone crate give {player} 1 in votes.rewards),
/keystone crate history <player> and /keystone crate test <rank> [rolls],
which rolls up to a million crates without giving anything and prints what
came out beside what was expected.
A better rank can improve the odds of a rarer pull, and nothing else.
crates.odds is one line per group, worst first — default: 70, 22, 7, 1,
vip: 66, 24, 8.5, 1.5, vip-plus: 62, 25, 10.5, 2.5, legend: 57, 26, 13, 4 — and a player rolls with the last line whose group they are in. A server that
writes only default gives everybody the same odds. Players can read all of it. What a rarity holds (crates.loot.common … .legendary, empty
until the owner writes them) is the same for everybody, in the same shapes as a
vote reward — a command, perk, rank — plus item <id> <n>, a weight in
front (3: …) and a label after |. A rarity with nothing in it cannot come
up, and the odds shown say so.
Items a server describes, with a look its own resource pack draws.
crates.items names them: <id> <item> [model=<n>] [look=<ns:path>] [colour=#rrggbb] [glint] <name> | <lore>. model= is the number a pack
matches on Minecraft 1.21.1, look= the model it names from 1.21.4, and with a
look the name and lore are also read from the pack in the player's language.
Such an item stacks to 64 and does not glint unless asked.
An item with a look is protected. It carries Keystone's mark in its custom
data, which no survival action can write. An anvil offers no result, and lets
nothing be taken, while either slot holds one — at every return of the anvil's
own logic, including the one NeoForge's AnvilUpdateEvent takes; a creative
player below operator level 2 cannot write one into a slot (the creative
inventory is the one place a client sends whole items); and one that enters an
inventory loses a name an anvil gave it elsewhere. So a server can treat it as
genuine.
Whatever does not fit in an inventory is dropped at the player's feet, where
only they can pick it up — the way /give does it. A crate opened in the tick
before somebody left is handed over on their next join. A legendary is
announced to the server (crates.announce).
Colour on a client with nothing installed. One reader for what an owner
writes about colour — the sixteen & colours and five styles, &#rrggbb for
any colour, &r, && for an ampersand — used everywhere Keystone draws:
chat lines, announcements, titles, the countdown bar, the tab list, kick and
ban screens and the refusals at the door. Until now only the tab list read
&, and everything else printed &6 as two characters. Text somebody else
wrote — a Discord message, a ticket — is printed as written, so &4[ADMIN]
typed on Discord does not arrive red. The rank goes in front of the name in
chat: a group's prefix and suffix are put on the player's display name,
which a vanilla client draws and which is not part of a signed message; where
a scoreboard team already prefixes the name, or FTB Ranks is installed,
Keystone leaves the name alone rather than doubling it.
A player's words can never look like the server's. Colour codes and
language parts are read only in text the owner wrote — settings, language
files, rank prefixes (Message.formatted in the API, new). Everything else —
a name, a ban reason, a Discord message, a ticket, an assistant's answer, any
Message.literal — is printed exactly as written: no colour, no line break,
no [xx] language part. Nothing in text can make a clickable or hover
component; only code builds those.
Each player picks their language. A server that adds a language file to
config/keystone/lang/ (naming itself with language.name=) asks every
player once, with a line to click, which language Keystone should speak to
them; /language changes it later. The choice is kept per player; until
somebody chooses, their game's language is used where the server offers it, and
language.default where it does not. language.offered narrows which languages
players may choose — ["cs"] makes a server Czech only, and nobody is asked.
English stays the floor for any line a translation lacks. The jar itself still
ships English only.
Translate in the editor. A Languages screen lists every language the
server has, how much of it is translated and which are offered; opens one with
every line beside the English, a filter for what is still untranslated, the
colours drawn as the game draws them and a placeholder the English does not
have marked (and refused when saving); starts a new one from a code and its
name; and exports and imports the .properties file. Saving writes
config/keystone/lang/<code>.properties and players read it at once. Writing
needs the owner's link — /keystone panel from an operator or the console;
a moderator who reaches it through a group gets a link without
languages.write.
An owner's own text in more than one language. Anything written in a
setting that a player reads — announcements, the kick, ban, full and
maintenance screens, the tab list — may carry a part per language:
[en]The server is full. [cs]Server je plný. Each player reads their part,
then the server's language.default, then English; text with no marker reads
as it always did.
A fresh server lets players use their own commands. The default group
used to start empty, so an ordinary player could not use /vote, /playtime,
/skin, /discord or /crate until an owner found out from a player. It now
starts with those; anything an owner already decided about them is left alone.
Discord. discord.status-channel says that the server is up, is going
down, or will restart in so many minutes (once per restart, not at every
warning) — by default ("chat") in the chat channel, where the players are; a
channel id sends it elsewhere and blank nowhere. news-channel is no longer
read: a news channel is for news. The list of channels and
roles is read at most once per half minute, however many of them change —
every channel edit, including the bridge's own topic, used to spend Discord's
ten-a-minute budget shared with everything else on the same bot.
Keystone's own failures can go to an error tracker — Sentry or
GlitchTip, from errors.dsn in keystone.toml. Off by default, and the
jar carries no address: nothing leaves a server unless its owner writes one.
Only failures with Keystone's code in their stack are sent, with the
exception type, its frames, the version and the loader, and the exception's
message only after every secret the server's settings hold, tokens and keys,
addresses, e-mails, URLs, file paths, UUIDs and anything quoted are removed —
no player names, addresses or chat; the same failure at most once in ten minutes, never more
than thirty a minute. Written without the Sentry SDK, whose global client
another mod on the same server could take over.
A leftover setting is cleaned up. 0.1.0 wrote announce() into
progress.toml beside the real announce; any key whose name ends in () is
now removed when a settings file is read.
Everything new is a setting, and takes effect without a restart.
territory.fly.* and territory.bonus are read on every pass (a bonus line
broken by an edit keeps the last table that read); the two limits on flying
anywhere are settings too — fly.nether-roof (128, 0 lifts it) and
fly.over-foreign-claims (off). keystone.toml gains language.ask-on-join,
chat.rank-in-name and chat.step-aside. crates.time-zone and
discord.news-channel are read when used, the news lines are language keys
(discord.news.up, .down, .restart) a server can reword, and
discord.names-every sets how often channels are re-read. /keystone reload
now also re-reads the server's language files and errors.*.
Restarts. The scheduled restart came back to nothing on a server started
where it lives: the game reports that directory as "", and a process told to
start in "" does not start, so the restart refused itself every night. It is
made absolute now. /keystone operations restart by hand no longer skips a
healthy server as "nothing to gain" — uptime, memory and TPS are reasons for
the clock, not conditions on an operator — while every other operation keeps
all its checks when run by hand, and one that throws or gives no answer does
not run. restart.after-uptime, .above-memory-percent, .below-tps and
.wait-for-empty now take effect when they are changed, as /keystone set
always said they did.
VIP works on the land FTB Chunks already keeps. A new module, territory
(config/keystone/territory.toml), reads claims and teams from FTB Chunks and
FTB Teams when they are installed — found at start, never required, and on a
server without them it says so once and stays quiet. Nothing of Keystone's own
claims is needed.
/flyover your own land. Somebody in a group named infly.groups(vip-plusandlegendby default), holdingkeystone.territory.fly, or an operator, turns it on with/flyand off the same way. It works only in chunks their own FTB team claimed — in every dimension — and switches off at the edge with the fall caught (slow falling until they land); it comes back by itself over their land. Allies count only withfly.allies = true. Creative and spectator are left alone, and flight another mod gave (a jetpack, a ring) is never taken away. A rank that ends grounds them on the next pass, even if it ended while they were offline.- More chunks per rank, and for time played.
bonusmaps a paid rank to extra claimed and force-loaded chunks (vip=25/1,vip-plus=50/2,legend=100/3by default) andearneddoes the same for playtime ranks (wanderer=5/0…, empty by default); the two layers add up, and inside each the best rank counts. Written into FTB Chunks' own limits so its claim screen shows the bigger number. It is applied when a rank starts, renews or ends and at every join, and Keystone moves only its own part of the number — extra chunks an admin gave by hand stay. A bonus line that does not read keeps the module off and the log says which. - Flight anywhere for some ranks.
fly.anywhere-groups(empty by default, orkeystone.territory.fly.anywhere) lets a rank fly outside its own land too — everywhere except a claim of another team that does not count it as an ally, and the nether roof from y 128 up. /keystone territory(keystone.territory.admin) says what land is read from;check <player>shows where they stand, their team and their extras;sync <player>applies the bonus now.
/spawn, warps and /vanish. A new module, essentials. /spawn and
/warp <name> (Tab offers the names; on its own, or /warps, it lists them)
for everybody — keystone.spawn and keystone.warp, which the default
group now starts with. /setwarp <name> makes or moves a warp where you stand
(keystone.warp.set), /delwarp removes one (keystone.warp.delete), and the
editor's Warps screen lists them and deletes them. A warp into a world that
is not loaded is listed as broken and refused with the reason. /vanish
(keystone.vanish) takes staff out of every other player's world and tab
list, out of the server list's count and names, the query port, /list, the
join and leave lines, the Discord bridge's join line and topic, and the counts
the local API gives out; mobs ignore them and, unless vanish.pick-up-items,
items stay on the ground. Whoever holds keystone.vanish.see still sees them.
It survives a reconnect and a restart. Each part can be switched off in
essentials.toml; where another mod already has /warp or /spawn,
Keystone's is under /keystone.
Who may rewrite what players read. /keystone panel gives the console
and operators a link to everything; anybody else who reaches it through a group
gets every scope but languages.write — and is refused, rather than handed
everything, when the list of scopes is not ready. Keys remember whether they
were issued to the owner, and writing a translation asks that as well as the
scope (a key made with keystone key new … * by a non-operator does not
pass).
A module that is off still shows its settings. The editor and the file
used to show only enabled for it, so crates could not be set up before the
day they went on. Its settings are listed now, under a note that it is off
(operations, moderation, skins, the tab list and the assistant still show only
enabled while off).
A command another mod already owns stays theirs. Keystone used to register its roots over whatever was there, which handed the other mod's command to Keystone under the other mod's permission check. It now leaves the root alone and says so in the log.
A kick shows its reason. The game closed the connection the instant the
disconnect packet left, with whatever the client had just sent still unread —
and Linux answers that by resetting the connection and throwing the packet
away, so a kicked player read Connection reset by peer instead of why. Every
kick, ban, maintenance close, restart and failed sign-in now reads the client
dry, sends the reason, then closes the sending half so the reason arrives
first. A Discord ban no longer disconnects somebody off the server thread.
Op is op. An operator (level 4, the level of /op and /stop) may use
everything; groups add on top and never take anything away from one. The
command tree and Player.has now ask the same rule — before, an operator could
be told they had a permission and then be refused the command. Operators below
level 4 are not owners here, on purpose: an owner who set
op-permission-level=2 limited them. A command block no longer passes just for
not being a player.
/kick and /ban could be used by anybody. With take-over-vanilla on
(the default), the game's own /kick, /ban, /pardon and /banlist were
done Keystone's way before the game checked who typed them. They now need
keystone.kick / keystone.ban like /keystone kick and /keystone ban.
And a moderator who is not op cannot kick or ban an operator, or anybody in a
heavier group than their own.
Confirming on Discord is remembered like a password — same remember-for,
same exact address (a keyed hash, never the address itself). 0.1.0 asked a
Discord-only player on every join and called it deliberate; it was a bug. A
remembered sign-in ends when the Discord link is removed or moved, on a ban, a
password change, keystone accounts forget, or "that is not me". An address
two accounts have proved themselves from — a shared house, every player behind
a proxy — is never remembered.
Until somebody has proved who they are, they hold nothing. On an
offline-mode server a name is all an operator's identity is, so a held player
now has no operator rights, no group and no permission, and the game drops
every packet from them except the few that keep the connection up and carry
/login: no chat, no blocks, no inventories, no creative menu.
Guessing passwords costs time, and cannot lock the owner out. Past
most-tries wrong passwords on a name from one place, each try waits twice as
long (30 seconds up to an hour). A name has a budget of ten wrong passwords an
hour from places it has never been used from, and then passwords from those
places rest for the hour; the owner's usual place is not held to it, and the
question on Discord is never held back by any of it. A place guessing at many
names is slowed, never locked. Somebody joining as you over and over no longer
pings you every time: one question a minute and five an hour from one place.
Descriptions say what ships. The mod's own description no longer promises quests, teams and claims.
A rank can be taken back over the API, and given to somebody who is away.
POST /v1/players/ungroup (permissions.write, fields player and group)
removes only the group grants the calling key made — what an admin typed, what
Discord granted and what another key gave stay — and answers
{player, group, removed}; removed: 0 is not an error. /v1/players/group
and /v1/players/ungroup now find a player who is not online but has joined
this server before, because ranks are bought and run out while people are
away. A name this server has never seen, or a group that does not exist, is a
400.