-
v4.6.2-rc3
Pre-releaseAll checks were successfulCI / Backend lint (ruff) (push) Successful in 29sCI / Which journeys does this change need? (push) Successful in 12sCI / Does this change touch the recording path? (push) Successful in 15sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 57sCI / Bot/backend version sync (push) Successful in 31sRelease / Create Forgejo release (push) Successful in 24sCI / Feedback site tests (push) Successful in 49sCI / Bot tests and audit (push) Successful in 2m6sCI / Docker image build (push) Successful in 3m21sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 8m31sRelease / Build and push versioned images (push) Successful in 12m48sCI / End-to-end journeys (desktop) (push) Successful in 11m6sCI / Frontend (shard 2/2) (push) Successful in 18m2sCI / Frontend (shard 1/2) (push) Successful in 22m43sCI / End-to-end journeys (phone) (push) Successful in 29m47sCI / Backend migration, tests, and audit (push) Successful in 33m46sCI / End-to-end journeys (keyboard) (push) Successful in 32m57sreleased this
2026-10-30 06:40:49 +00:00 | 307 commits to main since this releasePrerelease of 4.6.2 for testing on the dev server; not for production.
Added
- [repo] The feedback site has a name-rating page at
/names. Testers draw
names from the built-in generator (no AI), mark each up or down, and leave
notes per style; the admin readout tallies them (#1257). - [webapp] A map marker can be edited: its label, line, kind, page, map, holding, position and who sees it (#1260).
PATCH /api/campaigns/{id}/markers/{marker_id}. A GM edits any marker; a player edits only her own, as a note, place or danger, with no links. A marker's page can no longer be one in the wiki's bin, on create or edit. - [webapp] Maps and atlases can be renamed, changed and deleted into a bin, and travel links and trail stops can be removed (#1263).
PATCHandDELETEfor maps and atlases (GM only),GET /api/campaigns/{id}/maps-binwith restore routes, and a daily purge that removes the rows, pictures and tiles after the same grace period as the wiki bin.DELETEfor a travel link and for a trail stop; a travel link can be GM-only. Migrationf1263a0b1c2d. - [webapp] A map has a thumbnail, at most 1024 px on its longest side (#1263).
The worker writes it when it cuts the map's tiles, andGET /api/campaigns/{id}/maps/{map_id}/thumbserves it with the same audience check as a tile. A daily task writes it for maps cut before this release. - [webapp] The map, planned parts that were missing (#1263).
![[Map: X]]in
a page now shows a small picture of that map, linking to it. Place and
travel-link labels that would sit on top of each other no longer do: the
more important one keeps its label and the other shows on hover or focus.
A marker that opens a map of its own wears a "+" and says so in its name. A
group's page says "Holdings on the map" for the markers that name it as
holder. The party page shows where the party is and its trail, for the party
being viewed. - [webapp] The map, the rest of what was planned (#1263, #1260). A GM or a
marker's author can Edit a marker (what is here, kind, a line, who sees
it) and Move it; a GM also links it to a page ("The page it's about"),
a holder ("Held by", for a holding) and a map it opens. A travel-link
marker has Link to another map…, with a note and "Everyone at the
table" or "GMs only", and each link can be removed. Every marker a player
added says whose it is and who sees it, whatever its kind. A GM gets a
Maps in this atlas list with rename, level name, who sees it, Replace
the picture and delete for each map, atlas settings, and a Bin with
Restore. Each trail stop links to its session and a GM can remove one or
end the trail at a spot on the map; a campaign with two parties has a party
switcher; a party on a map below shows on the map above; the trail is drawn
across travel links; the party's own marker opens a small panel; the kind
chips are remembered. Every removal asks first, and every failure shows the
server's sentence with Try again. - [webapp] A trail stop reached by portal or magic has no line to it (#1273).
After a stop at a portal, "Add a stop" asks where the party came out: a
marker on any map (that portal's linked exits first), a spot, or nothing
yet, so a session can end with the party mid-portal. "No line to it: they
went by portal or magic" marks any stop the same way. The trail is drawn
broken there, and also where it changes map with no link to show the way.
Two portals on the same map can now be linked, such as the ways in and out
of somewhere with no map of its own. - [webapp] The party's trail: "Add a stop…", stops in an order a GM can change, and a shorter list (#1275).
"Where the party ended…" is now "Add a stop…". A GM moves a stop earlier or
later within its session. The list under the map is grouped by session,
newest first, with only the latest open.
POST /api/campaigns/{id}/parties/{party_id}/trail/{stop_id}/move.
Changed
- [repo] The feedback site's readout is tabbed (#1257). The name sampler
opens first, with a sub-tab per generator style holding its marks and notes;
the finished redesign rounds sit under "Completed: redesign". - [webapp] "On this map" stays short (#1276). The markers under the map
are grouped by kind, folded, with a "Find a marker" search that opens every
kind with a match. - [webapp] A "travel link" marker is now called a portal. The map's legend
says "Portals", a marker's kind is "A portal", and a marker made at the far
end of a link is "Portal to …". Any marker can still open a map of its own.
Fixed
- [webapp] The wiki editor's
[[link menu opens under the line you are
typing (#1261). It stood near the top of the text box whatever line you
were on, so on a page with text it covered the first lines and the link
being written. It now opens under the cursor's line, stays inside the
editor, and opens above the line when there is no room below. - [webapp] A map whose picture is refused is no longer made twice (#1263).
"Add a map" makes the map (and its atlas) once. If the picture is refused,
Try again sends only the picture; or the map is left without one and its
page offers "Add the picture". The![[Map: X]]embed loads the map's
thumbnail, falling back to the whole picture. - [webapp] Removing a marker that a trail stop uses no longer fails with a server error (#1263).
A stop made from a marker now keeps the marker's map and position, so it stays on the trail when the marker goes. The migration fills them in for existing stops. - [webapp] Choosing a page in the article picker is clicking its name (#1268).
The separate "Choose" link is gone, and on the map's marker form the name and
the link no longer run together as one word. - [webapp] The party's trail runs in session order (#1269). Stops were
ordered by when they were entered, so filling in earlier sessions after the
latest one moved "The party is here" back to the last one entered. A stop
made between sessions comes after the last session played when it was
entered. Migrationa1269b0c1d2eplaces the stops already recorded. - [webapp] "Where did the party end up?" finds a marker by searching its
name, and offers every kind (#1271). Dangers and notes were missing from
the list, and a dropdown of every marker would not have scaled to a long
campaign's map. With no marker chosen, it goes on to choose a spot. - [webapp] Embedding a map in a wiki page: the
[[menu finds maps, and the preview draws them (#1277).
AfterMap:the menu offers the campaign's maps by name, not "Create" a
page; a map whose name starts with what is typed is offered among the
pages. The editor's preview showed the embed as a page still to be written. - [webapp] A large map opens in moments, not 10–15 seconds (#1282). The
worker now cuts each map at half size, a quarter and so on, until the whole
map fits in one tile, and the page reads the level that fits the screen: an
8192 × 6144 map opened zoomed out needs a few dozen tiles, not all 768. Its
thumbnail shows at once underneath and sharpens as the tiles arrive. Maps
already uploaded get their levels from the same backfill as the thumbnails
(celery -A app.tasks.celery_app call app.tasks.map_tiles.backfill_map_thumbs),
and daily after that. - [webapp] The party's trail and "Add a stop", from the 4.6.2-rc2 review (#1283).
An older session in the trail list says "Show", and choosing a stop's place
on the map opens its session, so its stops' Remove and Earlier/Later are
easy to find. The marker searches in "Add a stop" list nothing until you
type, apart from a portal's linked exits, so the form stays short.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- [repo] The feedback site has a name-rating page at
-
v4.6.2-rc2
Pre-releaseAll checks were successfulCI / Which journeys does this change need? (push) Successful in 11sCI / Does this change touch the recording path? (push) Successful in 13sCI / Bot/backend version sync (push) Successful in 26sCI / Backend lint (ruff) (push) Successful in 31sRelease / Create Forgejo release (push) Successful in 25sCI / Feedback site tests (push) Successful in 47sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 58sCI / Bot tests and audit (push) Successful in 2m7sCI / Docker image build (push) Successful in 2m19sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 9m12sRelease / Build and push versioned images (push) Successful in 9m49sCI / Frontend (shard 2/2) (push) Successful in 13m22sCI / Frontend (shard 1/2) (push) Successful in 15m37sCI / End-to-end journeys (keyboard) (push) Successful in 10m53sCI / End-to-end journeys (desktop) (push) Successful in 12m1sCI / End-to-end journeys (phone) (push) Successful in 10m35sCI / Backend migration, tests, and audit (push) Successful in 50m16sreleased this
2026-10-29 21:57:58 +00:00 | 314 commits to main since this releasePrerelease of 4.6.2 for testing on the dev server; not for production.
Added
- [repo] The feedback site has a name-rating page at
/names. Testers draw
names from the built-in generator (no AI), mark each up or down, and leave
notes per style; the admin readout tallies them (#1257). - [webapp] A map marker can be edited: its label, line, kind, page, map, holding, position and who sees it (#1260).
PATCH /api/campaigns/{id}/markers/{marker_id}. A GM edits any marker; a player edits only her own, as a note, place or danger, with no links. A marker's page can no longer be one in the wiki's bin, on create or edit. - [webapp] Maps and atlases can be renamed, changed and deleted into a bin, and travel links and trail stops can be removed (#1263).
PATCHandDELETEfor maps and atlases (GM only),GET /api/campaigns/{id}/maps-binwith restore routes, and a daily purge that removes the rows, pictures and tiles after the same grace period as the wiki bin.DELETEfor a travel link and for a trail stop; a travel link can be GM-only. Migrationf1263a0b1c2d. - [webapp] A map has a thumbnail, at most 1024 px on its longest side (#1263).
The worker writes it when it cuts the map's tiles, andGET /api/campaigns/{id}/maps/{map_id}/thumbserves it with the same audience check as a tile. A daily task writes it for maps cut before this release. - [webapp] The map, planned parts that were missing (#1263).
![[Map: X]]in
a page now shows a small picture of that map, linking to it. Place and
travel-link labels that would sit on top of each other no longer do: the
more important one keeps its label and the other shows on hover or focus.
A marker that opens a map of its own wears a "+" and says so in its name. A
group's page says "Holdings on the map" for the markers that name it as
holder. The party page shows where the party is and its trail, for the party
being viewed. - [webapp] The map, the rest of what was planned (#1263, #1260). A GM or a
marker's author can Edit a marker (what is here, kind, a line, who sees
it) and Move it; a GM also links it to a page ("The page it's about"),
a holder ("Held by", for a holding) and a map it opens. A travel-link
marker has Link to another map…, with a note and "Everyone at the
table" or "GMs only", and each link can be removed. Every marker a player
added says whose it is and who sees it, whatever its kind. A GM gets a
Maps in this atlas list with rename, level name, who sees it, Replace
the picture and delete for each map, atlas settings, and a Bin with
Restore. Each trail stop links to its session and a GM can remove one or
end the trail at a spot on the map; a campaign with two parties has a party
switcher; a party on a map below shows on the map above; the trail is drawn
across travel links; the party's own marker opens a small panel; the kind
chips are remembered. Every removal asks first, and every failure shows the
server's sentence with Try again. - [webapp] A trail stop reached by portal or magic has no line to it (#1273).
After a stop at a portal, "Add a stop" asks where the party came out: a
marker on any map (that portal's linked exits first), a spot, or nothing
yet, so a session can end with the party mid-portal. "No line to it: they
went by portal or magic" marks any stop the same way. The trail is drawn
broken there, and also where it changes map with no link to show the way.
Two portals on the same map can now be linked, such as the ways in and out
of somewhere with no map of its own. - [webapp] The party's trail: "Add a stop…", stops in an order a GM can change, and a shorter list (#1275).
"Where the party ended…" is now "Add a stop…". A GM moves a stop earlier or
later within its session. The list under the map is grouped by session,
newest first, with only the latest open.
POST /api/campaigns/{id}/parties/{party_id}/trail/{stop_id}/move.
Changed
- [repo] The feedback site's readout is tabbed (#1257). The name sampler
opens first, with a sub-tab per generator style holding its marks and notes;
the finished redesign rounds sit under "Completed: redesign". - [webapp] "On this map" stays short (#1276). The markers under the map
are grouped by kind, folded, with a "Find a marker" search that opens every
kind with a match. - [webapp] A "travel link" marker is now called a portal. The map's legend
says "Portals", a marker's kind is "A portal", and a marker made at the far
end of a link is "Portal to …". Any marker can still open a map of its own.
Fixed
- [webapp] The wiki editor's
[[link menu opens under the line you are
typing (#1261). It stood near the top of the text box whatever line you
were on, so on a page with text it covered the first lines and the link
being written. It now opens under the cursor's line, stays inside the
editor, and opens above the line when there is no room below. - [webapp] A map whose picture is refused is no longer made twice (#1263).
"Add a map" makes the map (and its atlas) once. If the picture is refused,
Try again sends only the picture; or the map is left without one and its
page offers "Add the picture". The![[Map: X]]embed loads the map's
thumbnail, falling back to the whole picture. - [webapp] Removing a marker that a trail stop uses no longer fails with a server error (#1263).
A stop made from a marker now keeps the marker's map and position, so it stays on the trail when the marker goes. The migration fills them in for existing stops. - [webapp] Choosing a page in the article picker is clicking its name (#1268).
The separate "Choose" link is gone, and on the map's marker form the name and
the link no longer run together as one word. - [webapp] The party's trail runs in session order (#1269). Stops were
ordered by when they were entered, so filling in earlier sessions after the
latest one moved "The party is here" back to the last one entered. A stop
made between sessions comes after the last session played when it was
entered. Migrationa1269b0c1d2eplaces the stops already recorded. - [webapp] "Where did the party end up?" finds a marker by searching its
name, and offers every kind (#1271). Dangers and notes were missing from
the list, and a dropdown of every marker would not have scaled to a long
campaign's map. With no marker chosen, it goes on to choose a spot. - [webapp] Embedding a map in a wiki page: the
[[menu finds maps, and the preview draws them (#1277).
AfterMap:the menu offers the campaign's maps by name, not "Create" a
page; a map whose name starts with what is typed is offered among the
pages. The editor's preview showed the embed as a page still to be written.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- [repo] The feedback site has a name-rating page at
-
v4.6.2-rc1
Pre-releaseAll checks were successfulCI / Backend lint (ruff) (push) Successful in 29sCI / Feedback site tests (push) Successful in 44sCI / Docker image build (push) Successful in 13sCI / Bot/backend version sync (push) Successful in 50sCI / Does this change touch the recording path? (push) Successful in 14sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m7sCI / Which journeys does this change need? (push) Successful in 18sRelease / Create Forgejo release (push) Successful in 15sRelease / Build and push versioned images (push) Successful in 58sCI / Bot tests and audit (push) Successful in 1m59sCI / End-to-end journeys (phone) (push) Successful in 10m31sCI / End-to-end journeys (keyboard) (push) Successful in 11m9sCI / End-to-end journeys (desktop) (push) Successful in 11m14sCI / Frontend (shard 2/2) (push) Successful in 18m31sCI / Frontend (shard 1/2) (push) Successful in 22m21sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 23m44sCI / Backend migration, tests, and audit (push) Successful in 1h0m15sreleased this
2026-10-28 19:21:24 +00:00 | 337 commits to main since this releasePrerelease of 4.6.2 for testing on the dev server; not for production.
Added
- [repo] The feedback site has a name-rating page at
/names. Testers draw
names from the built-in generator (no AI), mark each up or down, and leave
notes per style; the admin readout tallies them (#1257). - [webapp] A map marker can be edited: its label, line, kind, page, map, holding, position and who sees it (#1260).
PATCH /api/campaigns/{id}/markers/{marker_id}. A GM edits any marker; a player edits only her own, as a note, place or danger, with no links. A marker's page can no longer be one in the wiki's bin, on create or edit. - [webapp] Maps and atlases can be renamed, changed and deleted into a bin, and travel links and trail stops can be removed (#1263).
PATCHandDELETEfor maps and atlases (GM only),GET /api/campaigns/{id}/maps-binwith restore routes, and a daily purge that removes the rows, pictures and tiles after the same grace period as the wiki bin.DELETEfor a travel link and for a trail stop; a travel link can be GM-only. Migrationf1263a0b1c2d. - [webapp] A map has a thumbnail, at most 1024 px on its longest side (#1263).
The worker writes it when it cuts the map's tiles, andGET /api/campaigns/{id}/maps/{map_id}/thumbserves it with the same audience check as a tile. A daily task writes it for maps cut before this release. - [webapp] The map, planned parts that were missing (#1263).
![[Map: X]]in
a page now shows a small picture of that map, linking to it. Place and
travel-link labels that would sit on top of each other no longer do: the
more important one keeps its label and the other shows on hover or focus.
A marker that opens a map of its own wears a "+" and says so in its name. A
group's page says "Holdings on the map" for the markers that name it as
holder. The party page shows where the party is and its trail, for the party
being viewed. - [webapp] The map, the rest of what was planned (#1263, #1260). A GM or a
marker's author can Edit a marker (what is here, kind, a line, who sees
it) and Move it; a GM also links it to a page ("The page it's about"),
a holder ("Held by", for a holding) and a map it opens. A travel-link
marker has Link to another map…, with a note and "Everyone at the
table" or "GMs only", and each link can be removed. Every marker a player
added says whose it is and who sees it, whatever its kind. A GM gets a
Maps in this atlas list with rename, level name, who sees it, Replace
the picture and delete for each map, atlas settings, and a Bin with
Restore. Each trail stop links to its session and a GM can remove one or
end the trail at a spot on the map; a campaign with two parties has a party
switcher; a party on a map below shows on the map above; the trail is drawn
across travel links; the party's own marker opens a small panel; the kind
chips are remembered. Every removal asks first, and every failure shows the
server's sentence with Try again.
Changed
- [repo] The feedback site's readout is tabbed (#1257). The name sampler
opens first, with a sub-tab per generator style holding its marks and notes;
the finished redesign rounds sit under "Completed: redesign".
Fixed
- [webapp] The wiki editor's
[[link menu opens under the line you are
typing (#1261). It stood near the top of the text box whatever line you
were on, so on a page with text it covered the first lines and the link
being written. It now opens under the cursor's line, stays inside the
editor, and opens above the line when there is no room below. - [webapp] A map whose picture is refused is no longer made twice (#1263).
"Add a map" makes the map (and its atlas) once. If the picture is refused,
Try again sends only the picture; or the map is left without one and its
page offers "Add the picture". The![[Map: X]]embed loads the map's
thumbnail, falling back to the whole picture. - [webapp] Removing a marker that a trail stop uses no longer fails with a server error (#1263).
A stop made from a marker now keeps the marker's map and position, so it stays on the trail when the marker goes. The migration fills them in for existing stops.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- [repo] The feedback site has a name-rating page at
-
v4.6.1
StableAll checks were successfulCI / Bot/backend version sync (push) Successful in 27sCI / Which journeys does this change need? (push) Successful in 18sCI / Does this change touch the recording path? (push) Successful in 19sCI / Feedback site tests (push) Successful in 51sCI / Docker image build (push) Successful in 27sRelease / Create Forgejo release (push) Successful in 9sRelease / Build and push versioned images (push) Successful in 37sCI / Backend lint (ruff) (push) Successful in 2m2sCI / Bot tests and audit (push) Successful in 2m4sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 2m28sCI / Frontend (shard 2/2) (push) Successful in 7m40sCI / Frontend (shard 1/2) (push) Successful in 10m27sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 26m37sCI / End-to-end journeys (phone) (push) Successful in 27m10sCI / End-to-end journeys (keyboard) (push) Successful in 29m17sCI / End-to-end journeys (desktop) (push) Successful in 29m36sCI / Backend migration, tests, and audit (push) Successful in 32m36sreleased this
2026-10-28 07:36:26 +00:00 | 353 commits to main since this releaseA point release: the map no longer goes blank while you place a marker, the GM
guide says what size of map works best, and the dependency updates since 4.6.0,
with security fixes in four packages. Tried on the dev server as4.6.1-rc1.Upgrading from 4.6.0
- No database migrations. Back up, then
docker compose pulland
docker compose up -d. BOT_CONTRACT_VERSIONis unchanged (2). The backend and the bot can be
updated one at a time.- Starter content packs are on this release's page too, so Admin → Content
packs keeps offering them.
Added
- [docs] The GM guide says what size of map works best (#1253). A new part of
"Prep" in Running your first campaign (/help/gm) covers where maps live,
the shape and size that suit the map frame (landscape, 3,000 to 4,000 pixels
on the long side), why a very large map is slow to open, and the limits on a
map's picture, including WebP's lower one.
Fixed
- [webapp] The map no longer goes blank when you press "Add a marker"
(#1251). Starting to place a marker wiped Leaflet's own classes off the
map, and without them every tile shrank to nothing; the picture stayed gone
until a reload. The map now stays visible while you pick the spot.
Security
- [repo] Dependency updates with security fixes: aiohttp 3.14.4, pypdf
6.21.2, Pillow 13 and authlib 1.9.1, along with the routine updates merged
since 4.6.0.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- No database migrations. Back up, then
-
v4.6.1-rc1
Pre-releaseAll checks were successfulCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 57sCI / Backend lint (ruff) (push) Successful in 33sCI / Bot/backend version sync (push) Successful in 22sCI / Feedback site tests (push) Successful in 47sCI / Does this change touch the recording path? (push) Successful in 15sCI / Which journeys does this change need? (push) Successful in 11sRelease / Create Forgejo release (push) Successful in 22sCI / Bot tests and audit (push) Successful in 2m2sCI / Docker image build (push) Successful in 5m1sRelease / Build and push versioned images (push) Successful in 5m30sCI / Frontend (shard 1/2) (push) Successful in 9m20sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 10m26sCI / Frontend (shard 2/2) (push) Successful in 17m30sCI / End-to-end journeys (desktop) (push) Successful in 13m40sCI / End-to-end journeys (phone) (push) Successful in 26m9sCI / Backend migration, tests, and audit (push) Successful in 33m44sCI / End-to-end journeys (keyboard) (push) Successful in 28m2sreleased this
2026-10-28 05:51:14 +00:00 | 354 commits to main since this releasePrerelease of 4.6.1 for testing on the dev server; not for production.
Added
- [docs] The GM guide says what size of map works best (#1253). A new part of
"Prep" in Running your first campaign (/help/gm) covers where maps live,
the shape and size that suit the map frame (landscape, 3,000 to 4,000 pixels
on the long side), why a very large map is slow to open, and the limits on a
map's picture, including WebP's lower one.
Fixed
- [webapp] The map no longer goes blank when you press "Add a marker"
(#1251). Starting to place a marker wiped Leaflet's own classes off the
map, and without them every tile shrank to nothing; the picture stayed gone
until a reload. The map now stays visible while you pick the spot.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- [docs] The GM guide says what size of map works best (#1253). A new part of
-
v4.6.0
StableAll checks were successfulCI / Does this change touch the recording path? (push) Successful in 20sCI / Which journeys does this change need? (push) Successful in 26sCI / Docker image build (push) Successful in 29sCI / Bot/backend version sync (push) Successful in 51sRelease / Create Forgejo release (push) Successful in 24sCI / Backend lint (ruff) (push) Successful in 1m1sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m29sCI / Feedback site tests (push) Successful in 1m37sCI / Bot tests and audit (push) Successful in 2m44sRelease / Build and push versioned images (push) Successful in 5m40sCI / Frontend (shard 2/2) (push) Successful in 9m49sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 10m19sCI / Frontend (shard 1/2) (push) Successful in 11m11sCI / End-to-end journeys (phone) (push) Successful in 22m42sCI / End-to-end journeys (keyboard) (push) Successful in 24m22sCI / End-to-end journeys (desktop) (push) Successful in 24m44sCI / Backend migration, tests, and audit (push) Successful in 33m50sreleased this
2026-10-18 06:52:52 +00:00 | 379 commits to main since this releaseThe in-world calendar, campaign templates and cloning, pictures brought in from a
web address with a Files page for each campaign, the line of the night, choosing
who was involved in an event, and the Pathfinder 2e and D&D 5e starter content
packs. Tried on the dev server as4.6.0-rc1and-rc2.Upgrading from 4.5.3
- Five database migrations, all additive (
a1191b0e5701,a1190e7e0a01,
a1181f11e5a1,a126ca1e0d01,a125c0b1e5e1). Back up first, then run
make migrate-safefrom the new release beforedocker compose up -d. BOT_CONTRACT_VERSIONis unchanged (2). Update the backend and the bot
together all the same: "In the story" and "Line of the night" on the approved
summary post are the bot's. An older bot keeps working without them.- Campaign exports are schema version 21. 4.6.0 imports older files; an
older Quest Board cannot import a 4.6.0 export. - Starter content packs. After upgrading, an administrator can fetch the
Pathfinder 2e (ORC) and D&D 5e SRD 5.2.1 packs from Admin → Content packs.
They come from this release's own page. Nothing is loaded automatically. - Pictures by address. The page-picture "from a web address" fetch now
takeshttpsonly, refuses compressed responses, checks every redirect, and
is rate limited (30 a person and 60 a campaign in ten minutes).
Added
- [webapp] The screens for bringing a picture in, and for a campaign's files (#1181). The wiki editor offers "Bring it in" for a picture written as an outside address (and from the picture button, by address), rewriting the text to the stored copy, with the server's plain reason when a fetch is refused. Wiki health lists "Pictures still on other sites" with a "Bring it in" and an Undo. Settings → Wiki has "Who can add pictures and files", and the editor hides the picture controls the server would refuse. Settings → Files lists a campaign's stored files as a grid or a table, with previews, filters, the source address, files nothing uses, a viewer with zoom, and Remove (showing the text that would break). Admin → Files lists them across campaigns by size, and Preview and Open are separate, recorded steps; the Privacy page says so.
- [webapp] Bring a picture in from a web address, and a Files page for each campaign and for admins (#1181). Anyone who can edit a page can have the server download a picture once and store it as an upload (JPEG, PNG, WebP or GIF, metadata stripped, never SVG); the source address and time are kept on the file and shown to GMs and admins only. Downloads count toward the campaign's storage, and are limited to 30 a person and 60 a campaign in ten minutes. A new campaign setting, "Who can add pictures and files" (everyone who can edit, or GMs only), is enforced on every upload route and on bring-it-in. Wiki health gains "Pictures still on other sites", with a "Bring it in" per page that rewrites the text as the GM's own edit. Campaign → Settings → Files lists every stored picture, map image, handout and attachment with previews, who added it, its address and whether anything still uses it, and removes them; orphans are listed apart. Admin → Files lists the same across campaigns by size, list only: opening or previewing another campaign's file is a deliberate act and is written to the audit log.
- [webapp] A GM can change who was involved when rewording an event. Reword can now also set an event's people (a character, a member, or an NPC page; or nobody, for scenery or a narrated happening). The change is saved with the reword, so it is recorded like one (who and when), survives a regenerate, and the next summary is composed from the corrected people. The event log and the summary's anchors show the corrected people, and the GM still sees who the model named. A GM-only search lists the campaign's characters and members first, then NPC pages.
- [webapp] The line of the night, on screen. Settings, General has "Line of the night: Chosen by the GM / Voted by the table". On a session's Review tab the GM marks, moves or clears the line of the night on an approved highlight, sees when voting closes, can close it now, and picks after a tie or a vote nobody cast. Players read the line of the night first with a small label, and while voting is open vote for a line (changing or withdrawing the vote), with counts hidden until it closes. The campaign Highlights page marks the lines of the night and can show only those.
- [webapp] The line of the night. A session can have one highlight marked as its line of the night. A campaign setting (Settings, General) says who picks it: the GM (the default), or the table by vote. In GM mode the GM marks, moves or clears one approved highlight. In vote mode every member, GMs included, votes once and can change the vote; voting opens when the GM approves the summary and closes three days later, or when the GM closes it early. Counts stay hidden until it closes, then the most votes wins, and a tie or no votes falls to the GM. The approved-summary post in Discord names a chosen quote as the line of the night. Exports carry the setting and each session's line of the night; imports accept files without them.
- [webapp] The Pathfinder 2e (ORC) and D&D 5e SRD 5.2.1 starter packs can be fetched from Admin → Content packs. Each release carries them as assets and the fetch address follows the running version, so a release candidate uses its own release. The reviewed
SOURCES.mdfor each pack is indocs/content-packs/4.6.0/. (#1200) - [webapp] Start a new campaign from an existing one, and save a campaign's
setup as a file (#125). A GM of the source campaign (an archived one is
fine) can copy its settings, game system, calendar, wiki page templates and
reputation levels, and the wiki pages and maps they tick, into a new campaign
they own, as a background job like an import. The world starts ticked
(places, organisations, deities, creatures, artifacts, concepts, historical
events, documents, NPCs); player characters, quests and pages made from a
session start unticked. GM-only text stays GM-only, personal notes are never
copied, links to pages left behind become plain words, everything that points
at a session is dropped (timeline events keep their in-world dates) and quests
come back not started. The campaign's own content packs are copied only with
the current affirmation, and the table's lines and veils only with "Invite the
same people", which sends each other member an ordinary single-use invitation
(a GM of the old campaign is invited as a GM); nobody is added directly. A
setup file is a campaign file with"setup_only": true: "Start from a file"
is the ordinary import. New routes:GET /api/campaigns/{id}/setup,
POST /api/campaigns/{id}/setup-file,POST /api/campaigns/{id}/clone.
One audit entry is written on each campaign. - [webapp] "A one-shot" accepts
one_shot: true, and is stripped back
further (#125). Its single reminder is now the day before (it was two
hours), the "who hasn't answered" nudge is off, and once its session has been
played the GM gets one "Needs you" suggestion to archive it
(archive_one_shot), answered with
POST /api/campaigns/{id}/archive-suggestion/dismissand never repeated. - [webapp] "Invite the same people" now sends each person their invitation. Starting a campaign from another with that ticked puts " invited you to ." in each other member's bell, and emails it where their settings say so, with a link to join. Nobody is added until they accept, and a used, expired or withdrawn invitation says so instead of linking (#125).
- [webapp] The screens for starting a campaign from another (#125). Settings → Archive has "Start a new campaign from this one…" and "Save the setup as a file…", one dialog for both: pick the wiki pages (the world starts ticked), maps, Lines & Veils, the campaign's own packs (after affirming them again), and whether to invite the same people. New campaign has "Start from a file" beside "Import a campaign". Needs you asks "Archive it?" for a played one-shot, the bell shows an invitation to a campaign with its join link, and an invitation link says when it was for a GM.
- [repo] The in-world calendar engine (#126, step 1; nothing user-facing yet). A calendar definition format (months, festival days and leap days with Gregorian-style rules, an optional "outside the week" flag for festival and leap days, week, moons, era; every entry has a stable id), validated by
app/schemas/world_calendar.py; pure arithmetic inapp/services/world_calendar.pyand, identically,webapp/frontend/src/calendar/engine.js(sort numbers, weekdays, moon phases, session ranges, formatting, and the check and date remapping for a calendar change). One shared file of test cases is run by pytest and vitest;scripts/calendar_cases.pyregenerates it from brute-force enumeration (--checkverifies it). Four generic presets. - [webapp] Date the story: the in-world calendar's screens (#126). Settings gains a Calendar section for GMs: start from one of four generic shapes, copy another campaign's calendar or load a file, then edit the months, festival and leap days (placed "after" a month, with leap rules and an "outside the week" tick), the week, the moons, the era and how a date reads, with a live preview and a check before saving that lists what a change would refuse or how many dates it would move. One date picker serves everything: a month grid with a column for each day of the week, festival days as their own cells, years before year 1 ("200 before DR"), the moons' phases as text, and the arrow, Home, End and Page keys; on the wiki it also offers Day, Month or Year with "About". A GM dates a session from Manage ("In the story…": the day it started, "The next day", hours or days covered, "Ran past midnight"), and the review offers one quiet row, "Where in the story?", with one press to save. Every member sees "In the story: 3–5 Mirtul, 1492 DR" under a dated session's head and on the Sessions list, and the campaign home says where the story has got to. A wiki timeline event can carry a date or a named age ("the Age of Ash", placed by choosing what it comes after), and the Storyline can show the timeline "As we played" or "In the world", with "9 days later" between sessions.
- [webapp] The in-world calendar's server side (#126; the screens follow). A campaign can have one calendar (
GET/PUT/DELETE /api/campaigns/{id}/calendar: every member reads it, only a GM writes), copied in from one of four generic presets (GET /api/calendar-presets), from another campaign the caller GMs, or from a JSON file (/calendar/file). A change is checked first (POST /calendar/check): removing a month or day that a session or wiki event is dated on is refused with the list of what uses it, and every other change moves the stored dates with their entries and recomputes the sort order in the same save. Sessions take an in-world start day and a span in hours or days (under 24 hours stays on the start day, plus "ran past midnight") through the ordinary session update, GM only; the session, the Sessions list and the campaign home carry the formatted range, the moons' phases on the start day, the campaign's "today", and a prefilled start for an undated session. Wiki timeline events take an in-world date at day, month or year precision (with "about"), or a named age with no year, and the Storyline's timeline read model carries each line's in-world label and sort number. The campaign export is now schema 21 and carries the calendar and every date; a version 20 file imports with no calendar. Additive migrationa126ca1e0d01. - [bot] The approved summary's Discord post says where it was in the story. When the session has in-world dates, the post gains a line "In the story: 3–5 Mirtul, 1492 DR" (additive:
extra.world_range, no contract bump). The text comes from names the GM typed into the calendar, so it is reduced to letters, digits, spaces and a few punctuation marks before it is posted.
Changed
- [webapp] Fewer read-only pages change anything when they are opened. Making a ready-made name set is now a POST (the GET only shows what is cached), the secrets health report no longer upgrades secrets (that happens at startup), an out-of-date ownership offer is expired by a background task rather than by looking at it, and the sheet pages no longer seed the built-in definitions on read (#1178).
- [webapp] The last gaps in stars and wishes (#122) and downtime (#124) are
closed. A GM sees the players' stars and wishes on the session's Review
tab, named only where the player signed. The star-and-wish ask goes only to
players marked as having attended, once any attendance is recorded. On the
party page a player edits or removes their own waiting downtime and a GM any
entry (and can approve it as they save it), with Undo after a removal. Downtime
totals now count approved days only, show "Days since Session N" per
character, and keep days still waiting apart as "(+3 waiting)". The downtime
list'stotalsgain apending_daysfigure, and theirdaysno longer
includes entries waiting for a GM. - [repo] Renovate moves the Playwright image and
@playwright/testtogether, in one PR, so a bump can no longer leave the journeys on an image without the browsers its version needs.
Fixed
- [webapp] The page-picture "from a web address" fetch now checks every redirect (#1181). It used to check only the address it was given, then follow redirects freely, so a public address could redirect the server to a private one. Each hop is now checked again, at most five are followed, a connection is made only to the address that was checked, and anything but a public address (carrier-grade NAT,
0.0.0.0, IPv4-mapped IPv6 and the like) is refused. - [webapp] Switching a campaign to "Voted by the table" no longer reopens voting on sessions already approved. They keep the GM's pick and show no vote; votes already cast are never deleted. "N still deciding" on This week counts players only, like the "Are you in?" line.
- [repo] The end-to-end journeys checks are named once each ("End-to-end journeys (desktop)"), not "(desktop) (desktop)". The job name no longer spells the matrix value, which Forgejo appends itself (#1203).
- [webapp] The hourly import sweep no longer breaks a slow "Start a new campaign from this one". It queued every waiting job as an import, which ended a clone with "uploaded file no longer on server". Jobs now carry a
kind(importorclone) and a clone keeps its choices, so the sweep queues the right task.GET /api/campaigns/import/jobs/latestand the job responses say which kind a job is, so the import page can tell them apart (#125). - [webapp] Six screen fixes from the 4.6.0-rc1 render check. Settings → Files shows each file as a stacked card on a phone and tells files on one page apart by size and date; the party page's Downtime and "What we carry" stack on a phone; the clone dialog keeps its buttons together in a footer that stays in view; the calendar date picker shows just the year when the era has no label; the review tab no longer treats "no summarisation run yet" as an error.
- [repo] A prerelease's image job no longer reports a failure after pushing every image. The
:latestpush was skipped with[ … ] && …, which made the step exit 1 for every prerelease. - [webapp] This week no longer counts the GM in "have answered". Under "Are you in?" it now reads "4 of 5 players have answered", "No players have answered yet." when only the GM has, and nothing at a table with no players, the same as the session page. Both pages print one shared line, so they cannot drift apart again. (#1202)
Security
-
[webapp] The check on addresses the server fetches pictures from now refuses IPv6 forms that hide a private IPv4 address (#1181). NAT64 (
64:ff9b::/96,64:ff9b:1::/48), 6to4 (2002::/16) and Teredo (2001::/32) are blocked outright, and the IPv4 address inside each, and inside an IPv4-mapped one, is checked as well.64:ff9b::7f00:1used to pass. The same check serves the page picture's "from a web address" fetch. -
[webapp] The server no longer decodes a compressed answer when it fetches a picture from a web address (#1181). It asks for the file uncompressed, refuses an answer that says it is compressed (a few KB of gzip could unpack to hundreds of MB before the size limit was checked), and counts the limit on the bytes that arrive. Multicast, site-local and IPv4-compatible addresses are refused as well, and the fetch ignores proxy settings in the environment.
-
[bot] multidict moves to 6.9.1, out of two advisories (PYSEC-2026-4244, CVE-2026-104874) that the daily dependency audit flagged on 15 and 16 Oct.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Five database migrations, all additive (
-
v4.6.0-rc2
Pre-releaseAll checks were successfulCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m4sRelease / Create Forgejo release (push) Successful in 15sCI / Which journeys does this change need? (push) Successful in 17sCI / Does this change touch the recording path? (push) Successful in 18sCI / Bot/backend version sync (push) Successful in 26sRelease / Build and push versioned images (push) Successful in 26sCI / Backend lint (ruff) (push) Successful in 35sCI / Feedback site tests (push) Successful in 58sCI / Bot tests and audit (push) Successful in 2m18sCI / Docker image build (push) Successful in 2m20sCI / Frontend (shard 2/2) (push) Successful in 9m58sCI / Frontend (shard 1/2) (push) Successful in 12m39sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 19m10sCI / End-to-end journeys (phone) (push) Successful in 22m5sCI / End-to-end journeys (keyboard) (push) Successful in 24m19sCI / End-to-end journeys (desktop) (push) Successful in 24m35sCI / Backend migration, tests, and audit (push) Successful in 41m44sreleased this
2026-10-17 21:59:38 +00:00 | 381 commits to main since this releasePrerelease of 4.6.0 for testing on the dev server; not for production.
Added
- [webapp] The screens for bringing a picture in, and for a campaign's files (#1181). The wiki editor offers "Bring it in" for a picture written as an outside address (and from the picture button, by address), rewriting the text to the stored copy, with the server's plain reason when a fetch is refused. Wiki health lists "Pictures still on other sites" with a "Bring it in" and an Undo. Settings → Wiki has "Who can add pictures and files", and the editor hides the picture controls the server would refuse. Settings → Files lists a campaign's stored files as a grid or a table, with previews, filters, the source address, files nothing uses, a viewer with zoom, and Remove (showing the text that would break). Admin → Files lists them across campaigns by size, and Preview and Open are separate, recorded steps; the Privacy page says so.
- [webapp] Bring a picture in from a web address, and a Files page for each campaign and for admins (#1181). Anyone who can edit a page can have the server download a picture once and store it as an upload (JPEG, PNG, WebP or GIF, metadata stripped, never SVG); the source address and time are kept on the file and shown to GMs and admins only. Downloads count toward the campaign's storage, and are limited to 30 a person and 60 a campaign in ten minutes. A new campaign setting, "Who can add pictures and files" (everyone who can edit, or GMs only), is enforced on every upload route and on bring-it-in. Wiki health gains "Pictures still on other sites", with a "Bring it in" per page that rewrites the text as the GM's own edit. Campaign → Settings → Files lists every stored picture, map image, handout and attachment with previews, who added it, its address and whether anything still uses it, and removes them; orphans are listed apart. Admin → Files lists the same across campaigns by size, list only: opening or previewing another campaign's file is a deliberate act and is written to the audit log.
- [webapp] A GM can change who was involved when rewording an event. Reword can now also set an event's people (a character, a member, or an NPC page; or nobody, for scenery or a narrated happening). The change is saved with the reword, so it is recorded like one (who and when), survives a regenerate, and the next summary is composed from the corrected people. The event log and the summary's anchors show the corrected people, and the GM still sees who the model named. A GM-only search lists the campaign's characters and members first, then NPC pages.
- [webapp] The line of the night, on screen. Settings, General has "Line of the night: Chosen by the GM / Voted by the table". On a session's Review tab the GM marks, moves or clears the line of the night on an approved highlight, sees when voting closes, can close it now, and picks after a tie or a vote nobody cast. Players read the line of the night first with a small label, and while voting is open vote for a line (changing or withdrawing the vote), with counts hidden until it closes. The campaign Highlights page marks the lines of the night and can show only those.
- [webapp] The line of the night. A session can have one highlight marked as its line of the night. A campaign setting (Settings, General) says who picks it: the GM (the default), or the table by vote. In GM mode the GM marks, moves or clears one approved highlight. In vote mode every member, GMs included, votes once and can change the vote; voting opens when the GM approves the summary and closes three days later, or when the GM closes it early. Counts stay hidden until it closes, then the most votes wins, and a tie or no votes falls to the GM. The approved-summary post in Discord names a chosen quote as the line of the night. Exports carry the setting and each session's line of the night; imports accept files without them.
- [webapp] The Pathfinder 2e (ORC) and D&D 5e SRD 5.2.1 starter packs can be fetched from Admin → Content packs. Each release carries them as assets and the fetch address follows the running version, so a release candidate uses its own release. The reviewed
SOURCES.mdfor each pack is indocs/content-packs/4.6.0/. (#1200) - [webapp] Start a new campaign from an existing one, and save a campaign's
setup as a file (#125). A GM of the source campaign (an archived one is
fine) can copy its settings, game system, calendar, wiki page templates and
reputation levels, and the wiki pages and maps they tick, into a new campaign
they own, as a background job like an import. The world starts ticked
(places, organisations, deities, creatures, artifacts, concepts, historical
events, documents, NPCs); player characters, quests and pages made from a
session start unticked. GM-only text stays GM-only, personal notes are never
copied, links to pages left behind become plain words, everything that points
at a session is dropped (timeline events keep their in-world dates) and quests
come back not started. The campaign's own content packs are copied only with
the current affirmation, and the table's lines and veils only with "Invite the
same people", which sends each other member an ordinary single-use invitation
(a GM of the old campaign is invited as a GM); nobody is added directly. A
setup file is a campaign file with"setup_only": true: "Start from a file"
is the ordinary import. New routes:GET /api/campaigns/{id}/setup,
POST /api/campaigns/{id}/setup-file,POST /api/campaigns/{id}/clone.
One audit entry is written on each campaign. - [webapp] "A one-shot" accepts
one_shot: true, and is stripped back
further (#125). Its single reminder is now the day before (it was two
hours), the "who hasn't answered" nudge is off, and once its session has been
played the GM gets one "Needs you" suggestion to archive it
(archive_one_shot), answered with
POST /api/campaigns/{id}/archive-suggestion/dismissand never repeated. - [webapp] "Invite the same people" now sends each person their invitation. Starting a campaign from another with that ticked puts " invited you to ." in each other member's bell, and emails it where their settings say so, with a link to join. Nobody is added until they accept, and a used, expired or withdrawn invitation says so instead of linking (#125).
- [webapp] The screens for starting a campaign from another (#125). Settings → Archive has "Start a new campaign from this one…" and "Save the setup as a file…", one dialog for both: pick the wiki pages (the world starts ticked), maps, Lines & Veils, the campaign's own packs (after affirming them again), and whether to invite the same people. New campaign has "Start from a file" beside "Import a campaign". Needs you asks "Archive it?" for a played one-shot, the bell shows an invitation to a campaign with its join link, and an invitation link says when it was for a GM.
- [repo] The in-world calendar engine (#126, step 1; nothing user-facing yet). A calendar definition format (months, festival days and leap days with Gregorian-style rules, an optional "outside the week" flag for festival and leap days, week, moons, era; every entry has a stable id), validated by
app/schemas/world_calendar.py; pure arithmetic inapp/services/world_calendar.pyand, identically,webapp/frontend/src/calendar/engine.js(sort numbers, weekdays, moon phases, session ranges, formatting, and the check and date remapping for a calendar change). One shared file of test cases is run by pytest and vitest;scripts/calendar_cases.pyregenerates it from brute-force enumeration (--checkverifies it). Four generic presets. - [webapp] Date the story: the in-world calendar's screens (#126). Settings gains a Calendar section for GMs: start from one of four generic shapes, copy another campaign's calendar or load a file, then edit the months, festival and leap days (placed "after" a month, with leap rules and an "outside the week" tick), the week, the moons, the era and how a date reads, with a live preview and a check before saving that lists what a change would refuse or how many dates it would move. One date picker serves everything: a month grid with a column for each day of the week, festival days as their own cells, years before year 1 ("200 before DR"), the moons' phases as text, and the arrow, Home, End and Page keys; on the wiki it also offers Day, Month or Year with "About". A GM dates a session from Manage ("In the story…": the day it started, "The next day", hours or days covered, "Ran past midnight"), and the review offers one quiet row, "Where in the story?", with one press to save. Every member sees "In the story: 3–5 Mirtul, 1492 DR" under a dated session's head and on the Sessions list, and the campaign home says where the story has got to. A wiki timeline event can carry a date or a named age ("the Age of Ash", placed by choosing what it comes after), and the Storyline can show the timeline "As we played" or "In the world", with "9 days later" between sessions.
- [webapp] The in-world calendar's server side (#126; the screens follow). A campaign can have one calendar (
GET/PUT/DELETE /api/campaigns/{id}/calendar: every member reads it, only a GM writes), copied in from one of four generic presets (GET /api/calendar-presets), from another campaign the caller GMs, or from a JSON file (/calendar/file). A change is checked first (POST /calendar/check): removing a month or day that a session or wiki event is dated on is refused with the list of what uses it, and every other change moves the stored dates with their entries and recomputes the sort order in the same save. Sessions take an in-world start day and a span in hours or days (under 24 hours stays on the start day, plus "ran past midnight") through the ordinary session update, GM only; the session, the Sessions list and the campaign home carry the formatted range, the moons' phases on the start day, the campaign's "today", and a prefilled start for an undated session. Wiki timeline events take an in-world date at day, month or year precision (with "about"), or a named age with no year, and the Storyline's timeline read model carries each line's in-world label and sort number. The campaign export is now schema 21 and carries the calendar and every date; a version 20 file imports with no calendar. Additive migrationa126ca1e0d01. - [bot] The approved summary's Discord post says where it was in the story. When the session has in-world dates, the post gains a line "In the story: 3–5 Mirtul, 1492 DR" (additive:
extra.world_range, no contract bump). The text comes from names the GM typed into the calendar, so it is reduced to letters, digits, spaces and a few punctuation marks before it is posted.
Changed
- [webapp] Fewer read-only pages change anything when they are opened. Making a ready-made name set is now a POST (the GET only shows what is cached), the secrets health report no longer upgrades secrets (that happens at startup), an out-of-date ownership offer is expired by a background task rather than by looking at it, and the sheet pages no longer seed the built-in definitions on read (#1178).
- [webapp] The last gaps in stars and wishes (#122) and downtime (#124) are
closed. A GM sees the players' stars and wishes on the session's Review
tab, named only where the player signed. The star-and-wish ask goes only to
players marked as having attended, once any attendance is recorded. On the
party page a player edits or removes their own waiting downtime and a GM any
entry (and can approve it as they save it), with Undo after a removal. Downtime
totals now count approved days only, show "Days since Session N" per
character, and keep days still waiting apart as "(+3 waiting)". The downtime
list'stotalsgain apending_daysfigure, and theirdaysno longer
includes entries waiting for a GM. - [repo] Renovate moves the Playwright image and
@playwright/testtogether, in one PR, so a bump can no longer leave the journeys on an image without the browsers its version needs.
Fixed
- [webapp] The page-picture "from a web address" fetch now checks every redirect (#1181). It used to check only the address it was given, then follow redirects freely, so a public address could redirect the server to a private one. Each hop is now checked again, at most five are followed, a connection is made only to the address that was checked, and anything but a public address (carrier-grade NAT,
0.0.0.0, IPv4-mapped IPv6 and the like) is refused. - [webapp] Switching a campaign to "Voted by the table" no longer reopens voting on sessions already approved. They keep the GM's pick and show no vote; votes already cast are never deleted. "N still deciding" on This week counts players only, like the "Are you in?" line.
- [repo] The end-to-end journeys checks are named once each ("End-to-end journeys (desktop)"), not "(desktop) (desktop)". The job name no longer spells the matrix value, which Forgejo appends itself (#1203).
- [webapp] The hourly import sweep no longer breaks a slow "Start a new campaign from this one". It queued every waiting job as an import, which ended a clone with "uploaded file no longer on server". Jobs now carry a
kind(importorclone) and a clone keeps its choices, so the sweep queues the right task.GET /api/campaigns/import/jobs/latestand the job responses say which kind a job is, so the import page can tell them apart (#125). - [webapp] Six screen fixes from the 4.6.0-rc1 render check. Settings → Files shows each file as a stacked card on a phone and tells files on one page apart by size and date; the party page's Downtime and "What we carry" stack on a phone; the clone dialog keeps its buttons together in a footer that stays in view; the calendar date picker shows just the year when the era has no label; the review tab no longer treats "no summarisation run yet" as an error.
- [repo] A prerelease's image job no longer reports a failure after pushing every image. The
:latestpush was skipped with[ … ] && …, which made the step exit 1 for every prerelease. - [webapp] This week no longer counts the GM in "have answered". Under "Are you in?" it now reads "4 of 5 players have answered", "No players have answered yet." when only the GM has, and nothing at a table with no players, the same as the session page. Both pages print one shared line, so they cannot drift apart again. (#1202)
Security
-
[webapp] The check on addresses the server fetches pictures from now refuses IPv6 forms that hide a private IPv4 address (#1181). NAT64 (
64:ff9b::/96,64:ff9b:1::/48), 6to4 (2002::/16) and Teredo (2001::/32) are blocked outright, and the IPv4 address inside each, and inside an IPv4-mapped one, is checked as well.64:ff9b::7f00:1used to pass. The same check serves the page picture's "from a web address" fetch. -
[webapp] The server no longer decodes a compressed answer when it fetches a picture from a web address (#1181). It asks for the file uncompressed, refuses an answer that says it is compressed (a few KB of gzip could unpack to hundreds of MB before the size limit was checked), and counts the limit on the bytes that arrive. Multicast, site-local and IPv4-compatible addresses are refused as well, and the fetch ignores proxy settings in the environment.
-
[bot] multidict moves to 6.9.1, out of two advisories (PYSEC-2026-4244, CVE-2026-104874) that the daily dependency audit flagged on 15 and 16 Oct.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v4.6.0-rc1
Pre-releaseSome checks failedCI / Bot/backend version sync (push) Successful in 28sCI / Which journeys does this change need? (push) Successful in 16sCI / Does this change touch the recording path? (push) Successful in 17sCI / Docker image build (push) Successful in 19sCI / Backend lint (ruff) (push) Successful in 38sRelease / Create Forgejo release (push) Successful in 15sCI / Feedback site tests (push) Successful in 56sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m10sRelease / Build and push versioned images (push) Failing after 1m15sCI / Bot tests and audit (push) Successful in 2m15sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 9m30sCI / End-to-end journeys (phone) (push) Successful in 14m32sCI / End-to-end journeys (keyboard) (push) Successful in 15m24sCI / End-to-end journeys (desktop) (push) Successful in 15m29sCI / Frontend (shard 2/2) (push) Successful in 20m23sCI / Frontend (shard 1/2) (push) Successful in 22m40sCI / Backend migration, tests, and audit (push) Successful in 1h5m33sreleased this
2026-10-17 17:48:48 +00:00 | 392 commits to main since this releasePrerelease of 4.6.0 for testing on the dev server; not for production.
Added
- [webapp] The screens for bringing a picture in, and for a campaign's files (#1181). The wiki editor offers "Bring it in" for a picture written as an outside address (and from the picture button, by address), rewriting the text to the stored copy, with the server's plain reason when a fetch is refused. Wiki health lists "Pictures still on other sites" with a "Bring it in" and an Undo. Settings → Wiki has "Who can add pictures and files", and the editor hides the picture controls the server would refuse. Settings → Files lists a campaign's stored files as a grid or a table, with previews, filters, the source address, files nothing uses, a viewer with zoom, and Remove (showing the text that would break). Admin → Files lists them across campaigns by size, and Preview and Open are separate, recorded steps; the Privacy page says so.
- [webapp] Bring a picture in from a web address, and a Files page for each campaign and for admins (#1181). Anyone who can edit a page can have the server download a picture once and store it as an upload (JPEG, PNG, WebP or GIF, metadata stripped, never SVG); the source address and time are kept on the file and shown to GMs and admins only. Downloads count toward the campaign's storage, and are limited to 30 a person and 60 a campaign in ten minutes. A new campaign setting, "Who can add pictures and files" (everyone who can edit, or GMs only), is enforced on every upload route and on bring-it-in. Wiki health gains "Pictures still on other sites", with a "Bring it in" per page that rewrites the text as the GM's own edit. Campaign → Settings → Files lists every stored picture, map image, handout and attachment with previews, who added it, its address and whether anything still uses it, and removes them; orphans are listed apart. Admin → Files lists the same across campaigns by size, list only: opening or previewing another campaign's file is a deliberate act and is written to the audit log.
- [webapp] A GM can change who was involved when rewording an event. Reword can now also set an event's people (a character, a member, or an NPC page; or nobody, for scenery or a narrated happening). The change is saved with the reword, so it is recorded like one (who and when), survives a regenerate, and the next summary is composed from the corrected people. The event log and the summary's anchors show the corrected people, and the GM still sees who the model named. A GM-only search lists the campaign's characters and members first, then NPC pages.
- [webapp] The line of the night, on screen. Settings, General has "Line of the night: Chosen by the GM / Voted by the table". On a session's Review tab the GM marks, moves or clears the line of the night on an approved highlight, sees when voting closes, can close it now, and picks after a tie or a vote nobody cast. Players read the line of the night first with a small label, and while voting is open vote for a line (changing or withdrawing the vote), with counts hidden until it closes. The campaign Highlights page marks the lines of the night and can show only those.
- [webapp] The line of the night. A session can have one highlight marked as its line of the night. A campaign setting (Settings, General) says who picks it: the GM (the default), or the table by vote. In GM mode the GM marks, moves or clears one approved highlight. In vote mode every member, GMs included, votes once and can change the vote; voting opens when the GM approves the summary and closes three days later, or when the GM closes it early. Counts stay hidden until it closes, then the most votes wins, and a tie or no votes falls to the GM. The approved-summary post in Discord names a chosen quote as the line of the night. Exports carry the setting and each session's line of the night; imports accept files without them.
- [webapp] The Pathfinder 2e (ORC) and D&D 5e SRD 5.2.1 starter packs can be fetched from Admin → Content packs. Each release carries them as assets and the fetch address follows the running version, so a release candidate uses its own release. The reviewed
SOURCES.mdfor each pack is indocs/content-packs/4.6.0/. (#1200) - [webapp] Start a new campaign from an existing one, and save a campaign's
setup as a file (#125). A GM of the source campaign (an archived one is
fine) can copy its settings, game system, calendar, wiki page templates and
reputation levels, and the wiki pages and maps they tick, into a new campaign
they own, as a background job like an import. The world starts ticked
(places, organisations, deities, creatures, artifacts, concepts, historical
events, documents, NPCs); player characters, quests and pages made from a
session start unticked. GM-only text stays GM-only, personal notes are never
copied, links to pages left behind become plain words, everything that points
at a session is dropped (timeline events keep their in-world dates) and quests
come back not started. The campaign's own content packs are copied only with
the current affirmation, and the table's lines and veils only with "Invite the
same people", which sends each other member an ordinary single-use invitation
(a GM of the old campaign is invited as a GM); nobody is added directly. A
setup file is a campaign file with"setup_only": true: "Start from a file"
is the ordinary import. New routes:GET /api/campaigns/{id}/setup,
POST /api/campaigns/{id}/setup-file,POST /api/campaigns/{id}/clone.
One audit entry is written on each campaign. - [webapp] "A one-shot" accepts
one_shot: true, and is stripped back
further (#125). Its single reminder is now the day before (it was two
hours), the "who hasn't answered" nudge is off, and once its session has been
played the GM gets one "Needs you" suggestion to archive it
(archive_one_shot), answered with
POST /api/campaigns/{id}/archive-suggestion/dismissand never repeated. - [webapp] "Invite the same people" now sends each person their invitation. Starting a campaign from another with that ticked puts " invited you to ." in each other member's bell, and emails it where their settings say so, with a link to join. Nobody is added until they accept, and a used, expired or withdrawn invitation says so instead of linking (#125).
- [webapp] The screens for starting a campaign from another (#125). Settings → Archive has "Start a new campaign from this one…" and "Save the setup as a file…", one dialog for both: pick the wiki pages (the world starts ticked), maps, Lines & Veils, the campaign's own packs (after affirming them again), and whether to invite the same people. New campaign has "Start from a file" beside "Import a campaign". Needs you asks "Archive it?" for a played one-shot, the bell shows an invitation to a campaign with its join link, and an invitation link says when it was for a GM.
- [repo] The in-world calendar engine (#126, step 1; nothing user-facing yet). A calendar definition format (months, festival days and leap days with Gregorian-style rules, an optional "outside the week" flag for festival and leap days, week, moons, era; every entry has a stable id), validated by
app/schemas/world_calendar.py; pure arithmetic inapp/services/world_calendar.pyand, identically,webapp/frontend/src/calendar/engine.js(sort numbers, weekdays, moon phases, session ranges, formatting, and the check and date remapping for a calendar change). One shared file of test cases is run by pytest and vitest;scripts/calendar_cases.pyregenerates it from brute-force enumeration (--checkverifies it). Four generic presets. - [webapp] Date the story: the in-world calendar's screens (#126). Settings gains a Calendar section for GMs: start from one of four generic shapes, copy another campaign's calendar or load a file, then edit the months, festival and leap days (placed "after" a month, with leap rules and an "outside the week" tick), the week, the moons, the era and how a date reads, with a live preview and a check before saving that lists what a change would refuse or how many dates it would move. One date picker serves everything: a month grid with a column for each day of the week, festival days as their own cells, years before year 1 ("200 before DR"), the moons' phases as text, and the arrow, Home, End and Page keys; on the wiki it also offers Day, Month or Year with "About". A GM dates a session from Manage ("In the story…": the day it started, "The next day", hours or days covered, "Ran past midnight"), and the review offers one quiet row, "Where in the story?", with one press to save. Every member sees "In the story: 3–5 Mirtul, 1492 DR" under a dated session's head and on the Sessions list, and the campaign home says where the story has got to. A wiki timeline event can carry a date or a named age ("the Age of Ash", placed by choosing what it comes after), and the Storyline can show the timeline "As we played" or "In the world", with "9 days later" between sessions.
- [webapp] The in-world calendar's server side (#126; the screens follow). A campaign can have one calendar (
GET/PUT/DELETE /api/campaigns/{id}/calendar: every member reads it, only a GM writes), copied in from one of four generic presets (GET /api/calendar-presets), from another campaign the caller GMs, or from a JSON file (/calendar/file). A change is checked first (POST /calendar/check): removing a month or day that a session or wiki event is dated on is refused with the list of what uses it, and every other change moves the stored dates with their entries and recomputes the sort order in the same save. Sessions take an in-world start day and a span in hours or days (under 24 hours stays on the start day, plus "ran past midnight") through the ordinary session update, GM only; the session, the Sessions list and the campaign home carry the formatted range, the moons' phases on the start day, the campaign's "today", and a prefilled start for an undated session. Wiki timeline events take an in-world date at day, month or year precision (with "about"), or a named age with no year, and the Storyline's timeline read model carries each line's in-world label and sort number. The campaign export is now schema 21 and carries the calendar and every date; a version 20 file imports with no calendar. Additive migrationa126ca1e0d01. - [bot] The approved summary's Discord post says where it was in the story. When the session has in-world dates, the post gains a line "In the story: 3–5 Mirtul, 1492 DR" (additive:
extra.world_range, no contract bump). The text comes from names the GM typed into the calendar, so it is reduced to letters, digits, spaces and a few punctuation marks before it is posted.
Changed
- [webapp] Fewer read-only pages change anything when they are opened. Making a ready-made name set is now a POST (the GET only shows what is cached), the secrets health report no longer upgrades secrets (that happens at startup), an out-of-date ownership offer is expired by a background task rather than by looking at it, and the sheet pages no longer seed the built-in definitions on read (#1178).
- [webapp] The last gaps in stars and wishes (#122) and downtime (#124) are
closed. A GM sees the players' stars and wishes on the session's Review
tab, named only where the player signed. The star-and-wish ask goes only to
players marked as having attended, once any attendance is recorded. On the
party page a player edits or removes their own waiting downtime and a GM any
entry (and can approve it as they save it), with Undo after a removal. Downtime
totals now count approved days only, show "Days since Session N" per
character, and keep days still waiting apart as "(+3 waiting)". The downtime
list'stotalsgain apending_daysfigure, and theirdaysno longer
includes entries waiting for a GM. - [repo] Renovate moves the Playwright image and
@playwright/testtogether, in one PR, so a bump can no longer leave the journeys on an image without the browsers its version needs.
Fixed
- [webapp] The page-picture "from a web address" fetch now checks every redirect (#1181). It used to check only the address it was given, then follow redirects freely, so a public address could redirect the server to a private one. Each hop is now checked again, at most five are followed, a connection is made only to the address that was checked, and anything but a public address (carrier-grade NAT,
0.0.0.0, IPv4-mapped IPv6 and the like) is refused. - [repo] The end-to-end journeys checks are named once each ("End-to-end journeys (desktop)"), not "(desktop) (desktop)". The job name no longer spells the matrix value, which Forgejo appends itself (#1203).
- [webapp] The hourly import sweep no longer breaks a slow "Start a new campaign from this one". It queued every waiting job as an import, which ended a clone with "uploaded file no longer on server". Jobs now carry a
kind(importorclone) and a clone keeps its choices, so the sweep queues the right task.GET /api/campaigns/import/jobs/latestand the job responses say which kind a job is, so the import page can tell them apart (#125). - [webapp] This week no longer counts the GM in "have answered". Under "Are you in?" it now reads "4 of 5 players have answered", "No players have answered yet." when only the GM has, and nothing at a table with no players, the same as the session page. Both pages print one shared line, so they cannot drift apart again. (#1202)
Security
-
[webapp] The check on addresses the server fetches pictures from now refuses IPv6 forms that hide a private IPv4 address (#1181). NAT64 (
64:ff9b::/96,64:ff9b:1::/48), 6to4 (2002::/16) and Teredo (2001::/32) are blocked outright, and the IPv4 address inside each, and inside an IPv4-mapped one, is checked as well.64:ff9b::7f00:1used to pass. The same check serves the page picture's "from a web address" fetch. -
[webapp] The server no longer decodes a compressed answer when it fetches a picture from a web address (#1181). It asks for the file uncompressed, refuses an answer that says it is compressed (a few KB of gzip could unpack to hundreds of MB before the size limit was checked), and counts the limit on the bytes that arrive. Multicast, site-local and IPv4-compatible addresses are refused as well, and the fetch ignores proxy settings in the environment.
-
[bot] multidict moves to 6.9.1, out of two advisories (PYSEC-2026-4244, CVE-2026-104874) that the daily dependency audit flagged on 15 and 16 Oct.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v4.5.3
StableAll checks were successfulCI / Backend lint (ruff) (push) Successful in 36sCI / Bot/backend version sync (push) Successful in 27sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 1m6sRelease / Create Forgejo release (push) Successful in 17sCI / Does this change touch the recording path? (push) Successful in 25sCI / Which journeys does this change need? (push) Successful in 24sCI / Feedback site tests (push) Successful in 1m30sCI / Bot tests and audit (push) Successful in 3m46sCI / Docker image build (push) Successful in 5m3sRelease / Build and push versioned images (push) Successful in 5m43sCI / Frontend (shard 2/2) (push) Successful in 7m53sCI / Frontend (shard 1/2) (push) Successful in 9m22sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 8m50sCI / End-to-end journeys (phone) (phone) (push) Successful in 20m13sCI / End-to-end journeys (keyboard) (keyboard) (push) Successful in 22m5sCI / End-to-end journeys (desktop) (desktop) (push) Successful in 22m12sCI / Backend migration, tests, and audit (push) Successful in 46m49sreleased this
2026-10-14 21:59:22 +00:00 | 454 commits to main since this releaseA wiki fix: merging a session's page into an existing one merges again, and a
draft says the page's GM-only text is kept. Tried on the dev server as
4.5.3-rc1.Upgrading from 4.5.2
- No database migration, and
BOT_CONTRACT_VERSIONis unchanged (2). Pull
the new images anddocker compose up -d.
Changed
- [webapp] A wiki draft says your GM-only text is kept (#1195). The draft shows the page as the table reads it and puts every GM-only block back where it was on approve, but never said so, so a GM-only note looked dropped. When the page has GM-only text, the draft now says: "Your GM-only text isn't shown here. It stays on the page when you approve."
Fixed
- [webapp] Merging a session's page into an existing wiki page merges again, or says it couldn't (#1195). The merge capped the AI's reply at 2,048 tokens, which a model that thinks before answering could spend entirely, and the empty answer fell back to pasting the new paragraph under the page while the draft read as merged. The reply is uncapped now, like every other wiki rewrite, and a merge draft whose AI call fails is marked failed with the page untouched, so it can be started over.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- No database migration, and
-
v4.5.3-rc1
Pre-releaseSome checks failedRelease / Create Forgejo release (push) Successful in 16sCI / Does this change touch the recording path? (push) Successful in 20sCI / Which journeys does this change need? (push) Successful in 22sCI / Docker image build (push) Successful in 21sCI / Bot/backend version sync (push) Successful in 24sCI / Backend lint (ruff) (push) Successful in 33sCI / Feedback site tests (push) Successful in 49sRelease / Build and push versioned images (push) Failing after 1m12sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 2m4sCI / Bot tests and audit (push) Successful in 3m26sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 11m13sCI / End-to-end journeys (phone) (phone) (push) Successful in 12m7sCI / End-to-end journeys (desktop) (desktop) (push) Successful in 13m14sCI / End-to-end journeys (keyboard) (keyboard) (push) Successful in 13m54sCI / Frontend (shard 2/2) (push) Successful in 15m9sCI / Frontend (shard 1/2) (push) Successful in 17m21sCI / Backend migration, tests, and audit (push) Has been cancelledreleased this
2026-10-14 21:25:16 +00:00 | 456 commits to main since this releasePrerelease of 4.5.3 for testing on the dev server; not for production.
Changed
- [webapp] A wiki draft says your GM-only text is kept (#1195). The draft shows the page as the table reads it and puts every GM-only block back where it was on approve, but never said so, so a GM-only note looked dropped. When the page has GM-only text, the draft now says: "Your GM-only text isn't shown here. It stays on the page when you approve."
Fixed
- [webapp] Merging a session's page into an existing wiki page merges again, or says it couldn't (#1195). The merge capped the AI's reply at 2,048 tokens, which a model that thinks before answering could spend entirely, and the empty answer fell back to pasting the new paragraph under the page while the draft read as merged. The reply is uncapped now, like every other wiki rewrite, and a merge draft whose AI call fails is marked failed with the page untouched, so it can be started over.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads