feat(fediverse): host your own polls (federate as AS2 Question)
A post can carry a poll that federates as an AS2 Question so remote (Mastodon)
followers vote from their own app; votes are tallied server-side and the fresh
counts are pushed back as Update(Question). Voting is fediverse-only; the site
shows live, read-only results. Complements the existing inbound poll support.
- src/config/database.js — posts.poll_json (our poll definition) + poll_votes
table (post_id, actor_uri, choice; UNIQUE) backing the tally + per-actor dedupe.
- src/services/ActivityPubService.js — parseOwnPoll/pollTally/ownPollView helpers;
buildNote emits a Question (oneOf/anyOf + replies.totalItems + endTime/closed +
votersCount) for a poll post; handleInbox records a ballot (Note with name +
inReplyTo our poll) before the reply path, deduped per actor; a debounced
Update(Question) pushes fresh counts to followers; votersCount added to AP_CONTEXT.
- src/services/Scheduler.js — closeExpiredPolls() marks a poll closed once its
endTime passes and pushes the final tally; runs on the existing 60s tick.
- src/routes/posts.js — parsePollForm() turns the editor fields into poll_json on
create/save (a poll with votes is frozen), passes poll_json to the federation
hooks, and hands the post page a render-ready ownPollView.
- src/views/pages/post-edit.ejs — poll section (options, multiple-choice, duration);
disabled once the poll has votes.
- src/views/pages/post.ejs — display-only poll with result bars + voter/close meta.
- src/services/i18n.js — poll.* + pedit.poll_* strings (nl/en/de).
- test/polls.test.js — Question shape, tally, percentages, closed state, AS2 term.
- CHANGELOG(.nl/.de).md — "Create your own polls" under Unreleased.
Co-Authored-By: Claude <noreply@…>