Repository navigation
Online tables: a poker tournament played on FinalTable (v1.2.0) - #74
Merged
Merged
Conversation
A connected app with a game key offers "Played: Online at FinalTable" in the event editor's poker bar (events.online_app_id). The organiser sets the table up from the event page: one press POSTs FinalTable's /api/games with the roster, the session's blinds, a per-game webhook secret and the event id, keeps what came back in the new finaltable_games table, and tells everyone on the guest list where to play. The section then shows the table's status and, for managers, start / pause / resume / take out of play / cancel. FinalTable reports back to the new www/finaltable_webhook.php, which verifies an HMAC over "<ms timestamp>.<body>" with that game's secret, dedupes on finaltable_deliveries, and writes bust-outs, re-entries and the final standings into the session's poker_players rows - the completion runs the same finish hook as Manage Game's Finish, so payouts and points come from this event's payout structure. New: www/_finaltable.php (client + shared helpers), www/finaltable_dl.php, www/finaltable_webhook.php, www/finaltable.js. Proven end to end against FinalTable-dev; all six SECURITY.md sweeps clean. Changelog entries sit under [Unreleased]; the release commit that renames them and bumps the version comes when this ships. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brings in the semantic-version change (v1.0.0) and the timer rebrand (v1.1.0), so this branch's diff is honest against what is live. Thirty-five files merged without a conflict; the two that clashed were the same stale "app version at time of writing" line on both sides. Both now read the released 1.1.0 — the bump to 1.2.0 belongs in this branch's release commit, per the routine written into CLAUDE.md at 1.0.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The panel shows a viewer less than it shows a manager, but finaltable_dl.php's state action authorised with event_visibility_sql() alone and then handed back FinalTable's answer whole. On a public or league event that predicate is true for every signed-in member, so anyone who could see the event could read the guest list by name, the join code, and - through last_status, cached locally after tournament.completed - the night's full finishing order with names, ids and prizes, straight past the organiser's "hide guest list" setting. The endpoint now draws the same lines the page does: the join code and link go to someone on the guest list (a manager, or an approved invite matching their account id), the roster and the seat-by-seat entrants go to a manager, and everyone who can see the event gets the clock plus a seated count - which is all finaltable.js ever rendered for them. The standings are stripped for everybody, since nothing client-side reads them; event.php renders the winner's name server-side and sends the rest to Manage Game. The webhook address goes to nobody: a browser has no use for it. Found by a security review of this branch, and checked both ways on the dev pair: a manager still sees the table, a member who can see the event but is not on the guest list gets the clock and nothing that names anyone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The merge from main left two [Unreleased] headings, one from each side; this collapses them, so the file reads Unreleased, v1.2.0, v1.1.0, v1.0.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Isorgcom
force-pushed
the
GameNight-FinalTable
branch
from
September 21, 2026 21:03
3f345ae to
e454c08
Compare
Isorgcom
marked this pull request as ready for review
September 21, 2026 21:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Added
game key offers Played: Online at FinalTable in the event editor's poker
bar (
events.online_app_id). The event page then carries a Played onlineat section: before the table exists, the organiser and managers see what
will be sent - the guest list (the organiser hosts; every approved invitee
with an account goes, one without is named as not sent), the blind
schedule, chips, seats, buy-in and re-entry - and a Set up the table
button. That one press POSTs FinalTable's
/api/gameswith the roster, thesession's blinds (the site's default preset when the game has none;
FinalTable's Standard when neither exists or the blinds are in cents), the
start (FinalTable takes seven days ahead; the page says when to come back),
a per-game webhook secret and the event id, keeps what came back in the new
finaltable_gamestable, syncs the roster into the session's players, andtells everyone on the guest list where to play with a new
online_tablenotification (a changes-category message with the join link). From then
on the section is the table's status - registering with its start, then the
level, blinds, time to the next level, players left, paused or an emptied
room - with Play at FinalTable for people with a seat, Watch for
anyone who can see the event, and copy buttons for both;
finaltable.jspolls
finaltable_dl.php?action=stateevery fifteen seconds while the tabis visible. Managers get Start now, Pause, Resume, Take out
of play beside each seated player and Cancel the game; a refusal is
shown in FinalTable's own words. After a finish or a cancel the table can
be set up again. Deleting an event with a live table calls the game off
first, and removing a member from a league or deleting their account signs
them out of every FinalTable that has a key. Documented in DOCS.md under
Online Tables and in the sign-in bridge section.
A new receiver,
finaltable_webhook.php, takes FinalTable's signeddeliveries - the clock starting, every level and break, pauses, every
bust-out and re-entry, the ending, and a heartbeat every five minutes - and
writes them the way Manage Game would by hand: a bust-out is a finish
position (provisional until late registration and re-entry close), a
re-entry clears it and counts as a rebuy, and the completion records every
place, marks who actually played as bought in, sets the session to
finished and runs the same finish hook as the Finish button, so
payouts, points, bounties and entry tickets come from this event's
payout structure, and the league table picks the night up like any
other. FinalTable's own prize figures stay on the status line only. A
player at the table with no row here (an invite deleted after setup) gets
one, with an invite beside it. A cancellation leaves the session as it
stood. Each delivery is remembered in
finaltable_deliveriesso a retry isanswered once;
cron.phpprunes it at thirty days.administrator makes the key on its Admin page (shown once); an admin pastes
it into the app's Game key field on Site Settings › Connected Apps, where
it is kept encrypted with
encrypt_value()(sso_apps.api_key) and shownafterwards only as Set or Not set, with a clear tick to remove it.
Test makes two requests that create nothing - the unkeyed status route,
then a keyed read of a game that does not exist - and reports FinalTable's
own sentence when the key is wrong or missing. The activity log records
that a key was set, tested or cleared, never the key.
Security
once, a millisecond timestamp within five minutes, a
sha256=signaturechecked with
hash_equals()over<timestamp>.<body>using that game'sown secret (48 hex characters from
random_bytes(), stored encrypted,never sent to a browser) before anything in the body is trusted, 404 for a
game this site does not hold, 401 for a bad signature (logged as a
warning), and a UNIQUE
(game_id, delivery_id)row as the dedupe lock. Itnever includes
auth.php. Outbound calls never follow a redirect, so thebearer cannot be carried to another host, and every one is made by a
person behind CSRF and
can_manage_event(). SECURITY.md has the section.Changed
per connected app that holds a game key; shown only when one does
(an admin without one sees where to paste it). Only a tournament can be
online - the choice is disabled for a cash game and the column is cleared
when an event stops being a poker tournament. The game type select now
re-runs the poker-field toggle on change.
🤖 Generated with Claude Code