asobi_player_export (asobi v0.72.6)
View SourceEverything core holds about one player, as one map.
The read half of asobi_player_erase, and it covers the same tables in the
same order, so "what would erasure delete" and "what would export return" stay
one list rather than two that drift.
Positive allowlists, never maps:without/2
Every row goes through a maps:with/2 projection, modelled on
asobi_ops_players:project/1. players carries hashed_password, so a
subtractive filter is one schema field away from exporting a credential;
player_tokens carries live bearer tokens, so the export reports that a
session existed and when, never the token itself.
The two tables with no foreign key
match_records.players is a jsonb list of ids and votes.votes_cast is a
jsonb object keyed by voter id. Neither has a foreign key, and both are records
about a player whatever the schema says about referential integrity, so both
are included - by jsonb containment and jsonb key existence respectively.
votes is projected differently from every other table here: the row holds
everybody's votes, so exporting votes_cast whole would hand one player the
private choices of everyone else in the match. Only this player's own choice
is lifted out. A privacy feature that leaks is worse than one that is missing.
zone_snapshots.entities is deliberately absent. It is opaque game-defined
state whose shape core does not know - a game may key entities by player id or
bury one inside entity state, and there is no projection core could apply to it
that would not either miss the data or export another player's. A game that
puts personal data there owns exporting it, the same way it owns erasing it.
No extension callback
There is deliberately no export_player/1 for extensions in this change.
asobi_extension:erase_player/1 earns its keep because the foreign key
physically forces an author to answer it; an export callback has no forcing
function, so an extension that skipped it would produce a silently incomplete
export and nothing would fail. That is a worse contract than no contract. If
one ever ships, the payload must name which installed extensions contributed
and which did not.
Summary
Functions
Every core row that names PlayerId, projected.