kaya-oidc 0.0.6 namespaces its session state per provider as
oidc:{provider}:user (the default provider is "default", so the key is
oidc:default:user). The unprefixed oidc_user key is no longer written:
left unadapted, every websocket upgrade would be rejected with 401 and
traced requests would silently lose their user.id attribute. Adopt it
with a session_user helper on tavolo.platform.auth that finds any
provider's user, used by both get_ws_user and telemetry's
session_tag_hook. Websocket and route handlers keep their signatures.
kaya-otel 0.0.6 only enriches websocket spans with standard URL, network
and peer attributes (plus error.type), which needs no application change.
Regenerate the lock: the eight kaya pins move to 0.0.6 and, the wheels'
declared metadata being unchanged, nothing else moves. tavolo-platform
now floors kaya-core>=0.0.6 (it reads the new session layout) and
tavolo-app floors kaya-otel>=0.0.6.
As kaya documents, the session-key move signs existing logged-in users
out once; they simply log in again.
tavolo
A platform for multiplayer card games. The first game is scopone scientifico — the four-player, fixed-partnership Italian card game:
server/— backend: Python + kaya framework, OIDC login, live games in Redis, match statistics in Postgres. A monorepo of three distributions sharing thetavolonamespace: the game-independenttavolo-platform, the scopone gametavolo-scopone, and thetavolo-appcomposition root wiring them together. See server/README.md.web/— frontend: Rust + Sycamore compiled to WebAssembly, built with Trunk and styled with Bootstrap 5.3 (MIT, vendored underweb/vendor/so builds work offline) plus the custom felt theme inweb/theme.css. Card images are the CC0 woodcut napoletane deck traced from a 1902 Naples print (SONDLecT/woodcut-napoletane).
Run the whole stack
docker compose up --build
Postgres, Redis, a mock OIDC provider (test users alice, bob, carol,
dave), the database migrator and the app all come up together. The app —
frontend and API — listens on http://127.0.0.1:8080.
Because both the browser and the app talk to the OIDC issuer at
http://mockoauth:8180/tavolo, add a host entry once:
echo "127.0.0.1 mockoauth" | sudo tee -a /etc/hosts
Between hands
When a hand ends but the match is not decided, the game pauses on a
scoring summary screen: every player sees how each category was won
(carte, denara, settebello, primiera, scope — plus napola and asso
pigliatutto when enabled)
with the running totals and
must click "Understood" before the next hand is dealt. If someone is away
the next hand is dealt automatically after HAND_ACK_TIMEOUT_SECONDS
(default 30s). The match-ending hand is explained on the final screen.
On your turn
Every turn shows a countdown (TURN_TIMEOUT_SECONDS, default 30s). If a
player does not move — disconnected or fallen asleep — the server plays a
random legal card for them (randomizing among the legal captures when the
rules require a capture), so one absent player cannot stall the table.
Development
Backend (from server/):
cd server
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install -e ./packages/tavolo-platform -e ./packages/tavolo-scopone -e './packages/tavolo-app[dev]'
.venv/bin/python -m unittest discover -s tests -t .
.venv/bin/python -m unittest discover -s packages/tavolo-platform/tests
.venv/bin/python -m unittest discover -s packages/tavolo-scopone/tests
Frontend (from web/), with the backend running on :8080:
cd web
trunk serve # SPA on http://localhost:8000, /api /auth /ws proxied
Trunk proxies /api, /auth and /ws to 127.0.0.1:8080 (see
web/Trunk.toml). For the login redirect to land back on the dev server,
run the backend with:
OIDC_POST_LOGIN_REDIRECT=http://localhost:8000/ \
OIDC_POST_LOGOUT_REDIRECT=http://localhost:8000/ \
.venv/bin/granian --host 127.0.0.1 --port 8080 tavolo.app:app
Card images are committed under web/assets/cards/; web/fetch-cards.sh
re-downloads them if needed.