Security & Trust
Assembley is designed from the ground up for governance — where every action must be auditable.
Verifiable secret voting
A secret ballot usually forces a trade: voters can trust the count, or they can verify it — rarely both. Four things are true of a secret ballot here at the same time.
Access is a single-use, time-limited ballot link scoped to one voter and one assembly. No passwords, no shared credentials.
Connections are encrypted in transit: HTTPS only, HTTP redirected, strict transport security set. Sessions are stored in httpOnly cookies invisible to JavaScript.
Row-level security separates each organisation's data, and roles separate what a voter and an administrator can reach. On a secret ballot the vote row carries no voter at all.
The database runs in Frankfurt and the application's functions in the same region. What the sub-processor agreements permit is wider, and two of them name the United States.
A hash-chained event log, a Merkle root per agenda item, and an evidence package a third party can recompute without access to our systems.
Administrative actions and governance events are appended to logs whose triggers refuse UPDATE and DELETE. Nothing watches them: the rate limiters refuse abuse, and refusing is not monitoring.
Assembley handles governance voting for companies, associations, and organisations across Europe. The security model is built around three things: controlled access, vote integrity, and a record that can be checked afterwards.
If a vote is challenged, you can produce the recorded chain of governance events — who voted, when, and in which order. For secret-ballot items the record attests participation and the ballot set, not who chose what. A trigger refuses UPDATE and DELETE on that chain and on the votes themselves, so it cannot be rewritten afterwards by anyone, including Assembley. That is not a policy commitment; it is enforced at the database level.
Identity
Access is a single-use link scoped to one voter and one assembly. No passwords. No shared credentials. What that link is worth is set by the organisation running the assembly.
Integrity
Votes are write-once at the database level. No administrator — including Assembley — can modify or delete a submitted vote.
Auditability
Governance events are recorded with a timestamp and the hash of the one before. The record is self-contained and independently verifiable.
The diagram above shows a single vote moving through Assembley's pipeline. When a voter submits their ballot, the server verifies the session against the assembly, writes the vote, computes a hash of the event, and appends it to the chain. On an identified ballot the vote and its audit entry are written in one database transaction, so neither can exist without the other. A secret ballot is the deliberate exception: it writes no audit entry, because the fields in one would name the voter.
Every participant is identified to the system before a governance action is accepted, and the system does not rely on passwords. What that identification is worth is a setting the organisation chooses.
How magic links work
A unique, cryptographically random ballot link is generated for each voter and sent to their registered email address.
The voter clicks their personal link — no typing, no password, no app required. Works on any device.
The link is validated server-side: checked for expiry, single-use status, and that it belongs to the correct assembly.
The link is invalidated on first use — it cannot be forwarded or reused, even if intercepted.
A session is issued, stored in an httpOnly cookie invisible to JavaScript, and used to authenticate every subsequent action.
The session is re-verified on every vote submission — not just at sign-in.
Identity assurance levels
Self-registration + one-time code
The voter registers themselves and confirms a one-time code sent to their address. A registration matching the roster votes with that member's weight; one that does not waits, and can cast nothing, until the chair admits it as a member.
Magic link + meeting code
The personal link is not enough on its own: the voter must also enter a code shown in the room while the assembly is live.
Magic link + meeting code + electronic ID
An electronic-ID check on top of both. No session is issued until the identity provider has answered — that callback is the only path that mints one.
New assemblies use this ladder. Assemblies created under the earlier scheme keep its five levels, and the level in force is recorded on the assembly and in its evidence package.
Rate limiting
Requests to re-send a ballot link are capped per IP address within a window.
A second link is not sent to the same address for the same assembly inside a cooldown.
One-time codes are capped per email address, with a separate ceiling per address sized for a room full of people on one network.
The re-send path answers identically whether or not the address is on the roster, so it cannot be used to test which addresses exist.
Link security
High-entropy random token — computationally unguessable
Single-use — invalidated immediately on first access
Time-limited — expires automatically after the assembly
Scoped to one assembly — cannot be used elsewhere
Session security
Session cookie is invisible to JavaScript — XSS-resistant
Secure and HTTPS-only under production — cannot be sent over HTTP.
SameSite=Strict, so the cookie is not attached to a request arriving from another site.
Sessions expire automatically — no persistent access
What entry records
Magic-link attempts are recorded with the address, the outcome and the time.
An electronic-ID verification enters the governance chain, carrying the assurance the provider actually returned — including when it returns none.
Entering a meeting code updates a presence row rather than appending an event, so a second entry overwrites the record of the first.
The level of assurance in force is recorded per assembly, not per claim about a person.
Where this stops
Votes are not stored as rows that get edited. Each is written once, and a trigger refuses UPDATE and DELETE on the table. Results are derived from what was written, not from a running total anyone can adjust.
Event sourcing model
Event written
Single INSERT
Hash computed
Over event payload
Appended to chain
prev_hash included
Vote submission — validation layers
Session verified
The voter's session is verified on every submission — not just at sign-in.
Assembly must be live
Votes are rejected if the assembly has not started or has already ended.
Voting window enforced
The administrator controls exactly when voting opens and closes for each agenda item. No votes are accepted outside this window.
One vote per item
Each voter can submit exactly one vote per agenda item. Duplicate submissions are rejected at the database level, not just in application code.
Vote and audit written together
The vote record and its audit entry are written in a single database transaction. A secret ballot is the exception and writes no audit entry, because the fields in one would name the voter.
A vote cannot be changed or deleted
Once written, a vote cannot be modified or deleted by anyone — including system administrators. This is enforced at the database level, not by access controls alone.
Merkle root updated
After each vote, the Merkle root for the agenda item is recomputed — any modification to any prior vote would change the root.
Event chain integrity
Each audit entry includes the hash of the previous entry — breaking the chain at any point is detectable.
Weighted voting
Each participant carries a voting weight set by the organisation.
Results are calculated on voting weight — not headcount.
Proxy votes are added to the representative's weight.
The administrator sees weighted counts as they arrive.
Administrator-controlled voting windows
Voting opens only when the administrator explicitly opens it
Closes immediately when the administrator moves on
Every voter is pushed the same state over a realtime channel.
Votes submitted outside the window are rejected server-side
What is frozen, and when
When an assembly opens, the figures it will be counted against are frozen: the total voting rights, the share capital basis, the shareholder count, and the quorum configuration. Each agenda item freezes its majority requirement and its secrecy when voting opens on it. The voter's name and weight are frozen onto the ballot as it is written, so a later change to the register cannot rewrite what was cast.
Atomic guarantees
An identified vote and its audit entry are written in a single database transaction; there is no state where one exists without the other. A secret ballot writes the ballot and its receipt in one transaction and no audit entry at all.
Every governance event is appended to a hash-chained log. Tampering with any historical record breaks the chain from that point forward — detectable by any party with access to the log.
Hash chain structure
Event N−1
hash: abc123
Event N
prev: abc123
hash: def456
Event N+1
prev: def456
hash: …
Each event's hash includes the previous hash — forming a tamper-evident chain.
Merkle roots
After voting closes, a Merkle root is computed from all votes
The root is a single hash representing the complete vote set.
Change any vote and the root changes — tampering is detectable
Roots are stored alongside the event chain, in a table whose trigger refuses UPDATE and DELETE.
Evidence package
Generated after the assembly closes
Contains the full event chain, Merkle roots, attendance, and results
The events file is stored uncompressed, so the SHA-256 of those bytes is the Verification ID printed on the PDF.
It carries the stored root, the recomputed root and the leaf scheme per item, so a third party can redo the arithmetic without access to Assembley.
Secret ballots — unattributed, still verifiable
Your own verification receipt
When you cast a secret ballot, your browser shows you a verification code to save. After voting closes, you can find that exact code in the published record and confirm your ballot was counted — without revealing how you voted. The code is deliberately non-transferable: it cannot prove your choice to anyone else, so it gives a vote-buyer or coercer nothing to verify.
Unattributed, verifiable evidence
For a secret item, the evidence package proves the result without naming a single voter: the final tally, the full set of unattributed ballot identifiers, a Merkle root fixing that exact set of ballots, a commitment to who was entitled to vote, and a reconciliation showing the number of ballots equals the number of recorded participants — the check that proves no one voted twice. An outside auditor can verify every line; none of it links a ballot to a person.
Where secrecy has a limit: where shareholders hold very different numbers of shares, an unusually large holding can sometimes be inferred from the published totals. The product says exactly that — on the ballot, before the voter votes. We would rather name the one place secrecy thins out than let it surprise someone later.
Events logged
Voter sign-in
IP address, email address, assembly, outcome, time
Failed sign-in attempt
IP address, email address, time
Vote submitted — identified ballot
Participant, agenda item, choice, IP address, device, time
Vote submitted — secret ballot
Agenda item, ballot code, choice. No voter, no address, no device
Assembly started / ended
Administrator, time
Voting opened / closed
Agenda item, administrator, time
Invitation sent
Recipient, assembly, time
Proxy registered
Grantor, representative, administrator, time
Document exported
Who took a copy, of which assembly, and the hash of what they hold
Where this stops
A single vote passes through five deterministic stages. Each stage is independently verifiable.
Voter session verified
Magic link token validated server-side, session issued.
Voting window confirmed
Server checks the assembly is live and the item is open for voting.
Vote event written
A single INSERT. A duplicate is rejected by a database constraint.
Audit entry appended
Written in the same transaction as the vote. A secret ballot writes a receipt instead, carrying the voter and not the choice.
Merkle root updated
Vote set for this agenda item rehashed and stored.
Four precise statements about what the system does. No qualifications, no marketing language.
Governance outcomes are not read out of a total anybody can edit. They are derived by replaying the append-only event log.
Auditability is architectural. It is not a feature added on top — it is the mechanism by which the system operates.
No administrator, including Assembley, can modify or delete a submitted vote. This is enforced at the database level, not by access controls.
The evidence package is self-contained. A lawyer or auditor can verify integrity without access to Assembley systems.
Assembley is designed so that the consequences of a failure are bounded and detectable. Votes are written once and every governance event is hash-chained, so a record altered after the fact breaks the chain at a point any reader can find.
What is watched, and what is not?
Nothing watches for unusual activity and no alert is raised. Failed sign-ins, rate-limit refusals and every administrative action are recorded and visible in the audit trail; reading them is something a person has to do.
What does the evidence package contain?
The full event chain, Merkle roots per agenda item, attendance records, vote results, and timestamps. The events file is stored uncompressed so its SHA-256 is the Verification ID on the PDF, and any modification after generation changes it. It can be verified by a lawyer or auditor without access to Assembley systems.
Can Assembley alter a submitted vote?
No. A trigger blocks UPDATE and DELETE on the votes table at the database level. This applies to every user including Assembley administrators. It is architectural, not a policy.
What happens if Assembley is unavailable?
Evidence packages generated before an outage remain valid and independently verifiable. The record does not depend on Assembley being online to be checked.
Assembley is built for European organisations. Data is stored in the EU, processed under a data processing agreement, and handled according to data minimisation principles. Where processing may occur is set by the sub-processor agreements rather than by a region setting, and those agreements are summarised in the published sub-processor list.
Data residency
The database runs in AWS eu-central-1, Frankfurt.
The application's functions run in the same region, moved there on 10 September 2026.
Transactional mail is sent from eu-west-1, Ireland.
Two of the three sub-processor agreements state that primary processing takes place in the United States. The sub-processor list carries both the agreement and the console reading, and does not resolve them into one sentence.
Data minimisation
Only the data needed to run an assembly is collected
The Services are designed to run on a name, an email address and a voting entitlement.
No tracking, analytics, or advertising.
Ballot links contain no personal information.
Encryption
All connections are encrypted in transit, with HTTP redirected and strict transport security set for two years.
Passwords, where they are used at all, are held only by the authentication provider and never by this application.
Ballot tokens are stored only as SHA-256 hashes, never in the form that was emailed.
Production credentials are not present in development environments or in anything sent to a browser. Every build counts the database hosts it has inlined and refuses to finish if one of them is not the local stack.
GDPR
The Data Processing Agreement is published, not available on request.
Right to erasure — a request is handled by the organisation as controller, and the share register is deletable by a super admin.
Data portability — full export available
No third-party data sharing without consent
What each role can access
What a voter can see
Their own identity and voting weight
The agenda and item descriptions
Their own submitted votes
Whether the assembly is live or ended
Other participants' identities or votes
Results before the assembly ends, where the organisation has chosen to withhold them
Administrative controls or settings
What an admin can see
The full participant list with voting weights
Aggregate vote counts as they arrive
Registered presence, and the figures the organisation configured to measure it against
The assembly event log and audit trail
Results after the assembly ends, and exportable minutes
Individual choices in a secret ballot — the ballot row carries no identity to look up
Any change to a vote once submitted
What Assembley does not do
Where this stops
Write to security@assembley.dk. Tell us what you found, how to reproduce it, and what you think it lets someone do. We do not publish a PGP key; if you need to send something encrypted, ask and we will arrange it.
Clause 34.3 of the Terms of Service makes testing conducted in accordance with this process an exception to the acceptable-use prohibition. Testing outside it is not covered.
What is in scope
What is out of scope
Rules
What we commit to
We do not offer a bounty. We would rather say so than leave it open.
Quoted from Annex II §10 of the Data Processing Agreement, which is the binding form of it. The measures on this page are described there; nothing here adds to them.
Assembley holds no third-party security certification as at the date of this DPA. The measures above are described as implemented; they are not certified by an external auditor, and Assembley makes no representation that they render Customer Personal Data secure against all forms of attack or compromise.