Skip to content

Sb/2c token health - #107

Closed
over2take wants to merge 11 commits into
mainfrom
sb/2c-token-health
Closed

over2take wants to merge 11 commits into
mainfrom
sb/2c-token-health

Conversation

@over2take

Copy link
Copy Markdown
Owner

Summary

Test plan

  • Tested locally
  • Tests pass

Pre-merge checklist

Code quality:

  • Code follows project style
  • No breaking changes (or clearly documented)
  • No console errors or warnings

Version & Release:

  • Version bumped? If releasing to users, update:
    • frontend/package.json version
    • docker-compose.yml APP_VERSION
    • CHANGELOG.md with release notes
  • GitHub Actions will auto-tag Docker images with the new version

Before merging to main:

  • All tests passing
  • PR reviewed and approved
  • Branch is up to date with main
  • No merge conflicts

Related issues

Developer and others added 11 commits September 29, 2026 10:23
A safe formula language (no eval: numbers, @fields, $rules, fixed functions,
lookup tables, capped size and nesting), a definition checker that reports
every problem with its place and shows loops as a path, dependency ordering,
and a small registry of code-backed rules.

Nothing live calls it. CWN's and Shadowrun's derived values are restated as
data and held to cwnRecompute and sr6Recompute by a parity test over 3,000
seeded sheets each; mutation checks confirm the test catches a wrong band, a
dropped rule, a wrong rounding and a wrong minimum.
…ocket events

Every token is signed with the same secret, and the checks only asked whether a
token was valid and not temporary, which a player's login token is. It passed
authenticate (delete buildings, GM notes, approve accounts, reset passwords) and
every admin socket check (socket admin at identify, grant editor rights, set any
bank balance, NPC users, purge dice history).

The checks now say what they mean: authenticate admits the GM or a granted
editor; authenticatePlayer adds a player's own login for their own sheet and
portrait; optionalAuthenticate treats players as anonymous; admin socket events
require the GM's own login. Three tests signed a GM token without the role the
real login always carries, and now sign it the real way.

Tests walk a player token against every route behind the GM check, and hold GM
login, granted editors, player sheet and portrait, and chat. Verified end to end
on a real server in secure mode.
…d the GM

grantElevatedAccess and approveEditing sent the new editor's token with
io.emit, so every connected client received a working key. They now send it
only to the target's own connections (the client already ignored grants for
anyone else, so the promoted player sees no difference). approveEditing,
denyEditing and revokeEditing had no check at all: a player could approve
their own edit request. They now require the GM or a granted editor, the same
people who see those buttons.

Tests run several connections on one server; mutation-checked. Verified on a
real secure-mode server: grant, use, revoke, surrender, approve and kick all
work, a bystander receives nothing, a player cannot approve themselves.
The trigger listed only main and dev, so PRs into a feature branch
(feature/system-builder and its sb/* pieces) were never tested until the
final merge to main.
…rmat and its checks

custom_systems keeps a draft the builder edits and the published copy a game
runs. Definition format 1 (name, description, words, parts, lookups, derived) is
checked on the server: fatal problems (not an object, too large, not JSON) refuse
storage; ordinary ones are saved in a draft and block publishing; all reported
with where they are. Ids are sys_ + hex and never collide with a built-in id.

GM-only routes at /api/systems (requireMainAdmin: granted editors are refused):
list, create from a name or a definition, read, save draft, publish, delete
(refused for the running system). Nothing in the game reads them yet (2d).

Tests: definition checks, words and parts defaults, the routes' rules, and the
auth walk now covers the systems routes; mutation-checked.
… with a database copy first

bank_accounts replaces the one-bank-per-player player_banks, which is kept
untouched as the record. Every bank read and write goes through
bank/accounts.js, scoped to the running system (a sheet's cash field shows its
own system's account), and waits for the one-time move to finish - browsers
reconnect within milliseconds of a restart, and an empty account opened first
would block a real balance from being copied in.

The move (startup/bankAccounts.js) copies the database first when the disk has
room (startup/backup.js, VACUUM INTO), then copies each balance, debt and flag
into every system the player has a sheet in plus the running one, in one
transaction, with a marker so it runs once.

Tests: accounts kept apart, the move's rules (mutation-checked: marker, wait,
systems, transaction), the copy and its space check, switching systems in play,
and db.js opening a 1.14.4-shaped file. The 15 test files that seeded
player_banks now use the running system's account. Verified on a read-only copy
of the real database: 21 banks into 29 accounts, every value identical.
… defense and injuries

The token's columns keep the running system's values, so combat, damage, the
health monitor and linked sheet fields are unchanged; the other systems' values
wait in token_vitals. tokens/vitals.js switchSystem swaps them and writes
game_system in one transaction, reading the system being left inside it, so a
failure or two quick switches can never split tokens from the setting. The
system picker route uses it and has every screen redraw; the generic settings
route can no longer change game_system or the migration markers.

A one-time start (startup/tokenVitals.js, adds rows only, run once) saves each
token's current values under every system it could be shown in. Map clears and
loads drop saved values for tokens that are gone, since they wind the id
sequence back.

Tests mutation-checked (restore, transaction, systems, settings guard, prune).
Verified on a read-only copy of the real database: 18 tokens switched away and
back came back identical.
…own folder

It matched the folder name MapSystem, which is only in a Windows checkout's
path: on the CI runner it cleared nothing, the second open of db.js returned
the first, closed connection, and the test failed with "Database is closed".
It now matches the backend directory as resolved, says so plainly if db.js was
not cleared, and waits for the first open's startup work (tokens/vitals
whenReady) before closing it.
@over2take over2take closed this Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant