Ownware
HomeVoteraManual
Votera · Manual

Votera Manual, as shipped in the download

Votera — User Manual

A public feedback board: posts, votes, statuses, a roadmap and a changelog. Version 3.1.2 [src: app/controllers/api.php:20].

About this manual

Every statement here was written by reading Votera's own source, and each one carries the file it came from in a bracketed src marker. Paths are relative to the folder this docs directory sits in. If a sentence disagrees with the application, the application is right and this manual has a bug — the markers exist so you can check rather than take our word for it.

Nothing is described that the code does not do. Where something is deliberately limited, that is said plainly rather than left out.

What Votera is

A feedback board people can post to and vote on, with statuses driving a roadmap and a changelog [src: app/src/Board.php:3].

Most of its routes are public, and that is the point: a feedback board nobody can reach without an account is not a feedback board.

Who a voter is — and who they are not

*Vote integrity is one row per post and voter, enforced by a unique index and* re-checked in code** [src: app/src/Board.php:4].

The identity behind that row is deliberately not a person:

In anonymous mode, a voter key is a random per-browser cookie token [src: app/src/Board.php:13], a year long, HTTP-only, and marked secure when the connection is [src: app/src/Board.php:19].

In email mode, the key is a salted hash of the address, never the address itself [src: app/src/Board.php:32]. The source states both consequences: one vote per email, and a leaked database still does not reveal who voted for what. The salt is the installation's own secret, so hashes are not portable between installations [src: app/src/Board.php:33].

An unrecognised mode setting fails safe to anonymous [src: app/src/Board.php:42].

⭐ Why a cookie alone is not enough — the measured attack

This is the most instructive passage in the product, and it deserves quoting rather than paraphrasing [src: app/src/Board.php:54]:

The measured attack was 1,239 votes a minute from one machine: the cookie key is client-supplied, so uniqueness on it is uniqueness of a number the attacker picks. A per-IP ceiling is what actually costs an attacker something.

That sentence names the flaw in the obvious defence. A unique index on a cookie value guarantees one vote per cookie, and the attacker chooses the cookie.

So there is a per-address rate limit applied in every mode [src: app/src/Board.php:50], defaulting to thirty votes an hour [src: app/src/Board.php:60].

The address is stored as a salted hash, never as the address [src: app/src/Board.php:50]. Old hits are pruned as the check runs [src: app/src/Board.php:69].

Two operational details worth knowing: setting the limit to zero disables it entirely, which the source notes restores the older behaviour for anyone who wants it [src: app/src/Board.php:52]; and a request with no address at all — a command-line or test context — is allowed rather than blocked [src: app/src/Board.php:65].

⭐ Vote velocity, and why it exists

A stuffed board and a popular board produce the same number. The difference is how fast the votes arrived, and the source records that this signal was present but invisible [src: app/src/Board.php:79]:

votes.created_at was always recorded and never surfaced anywhere, so a stuffed board looked exactly like a popular one.

The administrator now sees votes in the last hour and the last twenty-four hours [src: app/src/Board.php:85].

That is a good example of a fix that adds no new data — the evidence was already being collected, and the defect was that nobody could see it.

Boards, statuses and the roadmap

Multiple boards are supported [src: app/src/Board.php:92], each addressable by slug [src: app/src/Board.php:93].

Statuses are yours to define [src: app/src/Board.php:99], and a status carries a flag for whether it appears on the roadmap [src: app/src/Board.php:100] — so the roadmap is a filtered view of your own workflow rather than a fixed set of columns.

Posts can be sorted, filtered by board, and optionally include closed items [src: app/src/Board.php:119].

Users and roles

Three roles, every one enumerated with no wildcard [src: app/controllers/v3.php:17]:

Permissionviewermemberadmin
post.readyesyesyes
post.write — triage: edit, comment, mergeyesyes
post.moderateyes
changelog.writeyes
settings.writeyes

Note the split: a member can triage — edit, comment and merge — but cannot moderate [src: app/controllers/v3.php:21]. Merging duplicates is day-to-day curation; removing somebody's post is not.

⚠️ Backups are reachable by any signed-in user

Votera's database backup download is gated by sign-in alone, without a permission check [src: app/controllers/api.php:269].

A backup contains every table. On this product that includes the votes table with its voter keys and the hashed-address hit log — pseudonymous rather than personal, but still the raw material for correlating who voted for what within one installation. This has been raised as a defect against the product.

⚠️ The agent probe names the server differently from the specification

The REST interface names the server votera [src: app/controllers/api.php:20]; the unauthenticated transport probe names it Votera [src: app/controllers/v3.php:356].

Nothing behaves differently, but a client probing with GET and one calling with POST are told two different names. Match on the lower-case value. This has been raised against the product.

The API and agent access

Six tools are exposed to an agent [src: app/controllers/v3.php:191]: listing boards, listing posts, post detail and a top-voted report on the read side; creating a post and setting a status on the write side.

No tool casts a vote. An agent can raise an item and move it through your workflow; it cannot add to the count that decides what matters.

What Votera does not do

It does not store who voted for what in a form a leak would reveal [src: app/src/Board.php:32] — email mode stores a salted hash, and anonymous mode stores a random token.

It does not store voter IP addresses [src: app/src/Board.php:50], only salted hashes of them.

It does not rely on a cookie for vote integrity [src: app/src/Board.php:54], because the cookie is the attacker's to choose.

It does not let an agent vote [src: app/controllers/v3.php:191].

It does not let a member moderate [src: app/controllers/v3.php:21].

← Back to Votera · Quickstart · API · Test run

Affiliate program
Recommend tools people own — earn 35% on every sale. 90-day tracking, instant delivery, payouts by Lemon Squeezy.
Become an affiliate →