Skip to main content
Petanque Life

Elections & Voting

F01.03 8 features Shipped

At a glance

Elections & Voting handles the full election cycle for federation positions plus the digital meetings (AGM, board, committee, EGM) where those elections happen — eligibility resolution per OrgNode level, secret online ballots, proxy chains with anti-circularity guards, quorum checks, tie-break detection, versioned signed minutes and an append-only audit log built to satisfy national association law and FIPJP statutes.

How it works

An Election document carries scheduling, eligibility predicates and candidates. Voter eligibility is resolved per federation level: who can vote in an FFPJP national election differs from a club AGM, and the eligibility service consults the OrgNode tree, license status, membership age and any explicit denylist. When a candidate registers, their profile and platform are stored against the election.

Voting opens at the scheduled time. Each ballot is cast through the secure online voting endpoint, anonymised by separating the voter-identity check from the ballot-storage write — the ElectionAuditLog model validator and the service-layer _sanitize_details helper both enforce ballot secrecy so even a compromised audit reader cannot reconstruct individual votes. Proxy voting is layered on top: a voter can grant a proxy to another voter, but proxy chains are blocked (A→B→C is rejected), proxies can be limited to specific positions, max_proxies_per_voter is configurable per tenant and revocations carry a structured revocation_reason.

When voting closes the count runs with quorum verification and tie-break detection; results land at GET /public/elections/{id}/results. Around the election sits the Meeting domain (PL-T104) — a state machine draft → notice_sent → in_progress → concluded → minutes_draft → awaiting_signatures → signed. Each MeetingAgendaItem can include a voting widget with simple/2-thirds/3-quarters/consensus thresholds.

MeetingMinutes are versioned, content-hashed (SHA-256) and the signed PDF is WORM-archived. Signing runs through a pluggable adapter — BankID, Freja eID (qualified electronic signature), DocuSign, Clickwrap or eIDAS Wallet — gated by the tenant's SigningConfig.allowed_providers and require_qualified_signature flag. Two scheduled jobs (meeting-reminder-dispatch, minutes-signature-expiry) keep the workflow moving, and the Freja webhook is HMAC-SHA256-validated.

Key capabilities

  • Schedule and announce elections with eligibility rules tailored per OrgNode level.
  • Run secret online ballots whose audit trail provably cannot reconstruct individual votes.
  • Support proxy voting with chain prevention, position scoping and configurable per-voter caps.
  • Detect quorum failures and tied results during count and publish verifiable outcomes.
  • Drive AGMs, board meetings and EGMs through a notice-to-signed-minutes state machine.
  • Sign minutes with qualified electronic signatures via BankID, Freja eID, DocuSign or eIDAS.

In practice

An SBF club secretary needs to convene an extraordinary general meeting on a Friday evening to approve revised statutes. She creates the Meeting in state draft, attaches the agenda item with a 2-thirds voting widget, and triggers notice_sent — the platform mails every eligible member with a unique join link. Two members cannot attend and grant her their proxies (chain block engages and stops a third member from re-delegating).

At the meeting, in_progress, she opens the vote — 18 in favour, 4 against, 2 abstain, 2-thirds threshold met. Minutes are drafted, content-hashed, and routed to the chair and two adjusters for Freja eID signing. When the third signature lands, the WORM-archived PDF replaces the draft and the meeting transitions to signed.

The ElectionAuditLog and minutes-signature-expiry job take over from there.

Features in this subsystem

8
ID Status Features
F01.03.01 Shipped Election scheduling and announcement ✅ PL-F0103a
F01.03.02 Shipped Candidate registration and profile ✅ PL-F0103a
F01.03.03 Shipped Voter eligibility rules per federation level ✅ PL-F0103a
F01.03.04 Planned Secure online voting (anonymous, verifiable) Två saker: MÖTETS omröstning per dagordningspunkt (öppen/sluten, anonymitet som DB-invariant — saltad nyckel vars salt förstörs vid stängningen) är byggd i d5db9173.m-2 (se F01.03.08). Det FRISTÅENDE förbundsvalet (val.astro: röstlängd, verifieringskoder, *verifiable*) är ⊘ ägarbeslut — inget initiativ äger det. Legacy PL-F0103a är avvecklad FastAPI
F01.03.05 Planned Vote counting and result publication Mötets rösträkning är byggd i d5db9173.m-2: fyra majoritetsregler räknade exakt rationellt, fruset vote_result, serverns utfall per punkt, styrelsevalets pluralitet med tie. Publicering av resultat utanför mötet och det fristående valet: ⊘ ägarbeslut / protokollet d5db9173.m-4. Legacy PL-F0103b avvecklad
F01.03.06 Planned Proxy voting support Fullmakten på ett möte (d5db9173.m-1) och fullmaktsrösten som egen rad, med grantor-direktröstspärren (d5db9173.m-2), är byggda. Fullmakt i det fristående valet: ⊘ ägarbeslut. Legacy PL-F0103b avvecklad
F01.03.07 Planned Audit trail on voting processes Mötets omröstning och val dubbelskrivs (audit_log + meeting_event, GOV_MEETING_VOTE_*/GOV_MEETING_ELECTION_*), blinda för slutna röster (d5db9173.m-2); granskningsloggen GET /v1/governance/audit-log är byggd i d5db9173.m-5 (API, task t-1: mötenas tidslinje tvärs scope, familjefilter governance.EventFamily, aktören aldrig e-post; admin-fliken i task t-2); det fristående valet ⊘ ägarbeslut. Legacy PL-F0103b avvecklad
F01.03.08 Shipped Governance meetings (AGM, board, committee) with quorum, voting, minutes and QES signing ✅ d5db9173.m-1 — mötesryggraden: stadgekonfigen som ETT dokument per tenant ovanpå en kod-deklarerad nationskatalog (SE/FR/BE/NO/FI/DK/FIPJP + baslinje, källa per nyckel, nodöverlägg), rules_snapshot fryst vid skapandet, livscykeln draft → notice_sent → in_progress → concluded (+ cancelled) med fyra verb, kallelsen med frist (422 notice_period_too_short, loggad override), agenda-låset, deltagarlistan med import ur club_membership, fullmakten med tak och dubbelröstningsskydd, quorum-grinden 409 quorum_not_met ur ComputePresentHeadcount (ENDA räkningen), GOV_MEETING_*-paritet i audit_log + meeting_event, svepen governance_meeting_reminder_tick/governance_notice_deadline_guard. ✅ d5db9173.m-2 — omröstning och styrelseval: omröstningen per beslutspunkt (vote:open|vote|vote:close, fyra majoritetsregler över math/big, abstain utanför nämnaren, öppen/sluten valsedel, fullmaktsröst som egen rad, dubbelröstning omöjlig genom unique (punkt, represented_key_hash) med saltet förstört vid stängningen, fruset append-only vote_result, inget aggregat medan en sluten omröstning är öppen), styrelsevalet (board_election nominating → voting → closed, kandidater, valsedlar med upp till seats namn, pluralitet, tie löst av ordföranden med motivering), serverns outcome och per-punkt allowed_actions, conclude-grinden 409 vote_still_open, seedens femte möte. Kontrakt: specs/api/endpoints/governance-voting.md, governance-elections.md. ✅ d5db9173.m-3 (API, task t-1) — motioner och medlemmens väg: motionen som egen resurs med livscykeln inlämnad → under beredning → beredd → på dagordningen → avgjord (+ tillbakadragen), motionstiden som stadgekonfig relativ kallelsen (motion_deadline_at, kansliets loggade override), utfallet bifall/avslag skrivet ENBART av vote:close på motionspunkten (en motionspunkt är röstbar som en beslutspunkt), bordläggning och conclude-bordläggning, synligheten (andras motioner först när de är beredda), bilagor genom medieporten med MÄTT sha256/mime/storlek, /v1/me/meetings* (tenantlös läsning för deltagaren, motionstidens reducerade utkast), RSVP skild från närvaron, kallelsens bilagor genom D-COMM:s exporterade serveringssöm, tre nya notistyper och medlemmens djuplänk /moten, seedens sjätte och sjunde möte. Kontrakt: specs/api/endpoints/governance-motions.md, me-meetings.md. Appens /moten och admin-sektionen *Motioner* levereras av d5db9173.m-3 task t-2. ✅ d5db9173.m-4 (API, task t-1) — protokoll och e-signering: det versionerade protokollet med content_hash per version och omutabla låsta/signerade versioner, signeringen som F1-6-request minutes_signature med fyra villkorade steg (justerare 1–2, ordförande, sekreterare) tilldelade namngivna signerare och F1-6:s additiva AssigneeOnlyGuard (personlig signatur), OnApprove som ENDA väg till signed, OnReject ⇒ ny version, metodkatalogen (bara clickwrap i drift; QES-metoderna go-live) och signing-blocket i stadgekonfigen, den deterministiska arkiv-PDF:en genom blob-porten (governance_minutes_archive_tick), signatur-svepet governance_minutes_signature_expiry, styrelseval-write-back till nodens org_nodes.board genom D-ORGS UpdateOrgNode (MERGE per roll), operatörens GET /v1/sys/governance/overview, GDPR keep(legal_archive), fyra notistyper och seedens åttonde–tionde möte. Kontrakt: specs/api/endpoints/governance-minutes.md, sys-governance.md. Admin-protokolleditorn och sys /halsa/styrning levereras av d5db9173.m-4 task t-2. ✅ d5db9173.m-5 (API, task t-1) — beslut, åtgärder, publicering och granskningslogg: meeting_decision med livscykeln utkast → antaget → publicerat ⇄ antaget / upphävt, utfallet HÄRLETT ur vote_result eller registrerat, registret fruset under signeringen, antagandet som OnApprove:s FJÄRDE plikt med referensnummer <ÅÅÅÅ>-<NNN> ur tenantens radlåsta årsserie, omutabla antagna beslut (DDL-trigger); action_item med ansvarig (medlem), frist och FSM, den ansvariges /v1/me/action-items (start/complete), overdue som serverns dom och svepet governance_action_item_followup (påminnelse + eskalering, single-fire som DB-invariant); publiceringen som verb med publik ur stadgekonfigens publication-block (opt-in) fryst på raden, de två läsfamiljerna (anonym public, tenantbunden members) genom EN audiensregel och EN PII-minimal projektion, öppna kallelser ur den effektiva notices_audience; granskningsloggen; deltagarens besked governance_minutes_available och läsväg; GDPR (meeting_decision keep(legal_archive), action_item avidentifierad); fyra notistyper, seedens elfte och tolfte möte. Kontrakt: specs/api/endpoints/governance-decisions.md, public-governance.md. Admin-flikarna och webbens /styrning levereras av task t-2, appens *Mina åtgärder* av task t-3. ⊘ signering i appen (F1-6 → ägarbeslut). Kontrakt: specs/api/endpoints/governance-meetings.md §Greenfield (legacy: PL-T104, avvecklad FastAPI)