Tokora Manual, as shipped in the download
Tokora — User Manual
A waiting queue: numbered tokens, counters, a kiosk and a display board. Version 3.1.4 [src: app/controllers/api.php:22].
About this manual
Every statement here was written by reading Tokora'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 Tokora is
A waiting queue — numbered tokens, counters and call state [src: app/src/Queue.php:11].
It never touches money: no prices, no payments, no cards, no crypto, and the source notes there is no monetary field anywhere in the product [src: app/src/Queue.php:12].
Numbering, and the daily reset with no cron
Ticket numbers are simply the highest issued so far plus one, scoped to one service on one service day [src: app/src/Queue.php:81].
That scoping is the daily reset, and the source explains why it is the whole of it: a fresh service day has no prior numbers, so the sequence restarts at 1 — no cron, no counter to roll over, and recomputing is idempotent [src: app/src/Queue.php:82].
Nothing has to run at midnight. Nothing can fail to run at midnight.
Two kiosks at once cannot issue the same number
The number is chosen as a maximum plus one, which on its own would race. The database makes that impossible: a unique index means a concurrent double-issue cannot be committed, so on the rare collision the issuer recomputes and retries [src: app/controllers/app.php:65].
The retry is bounded at six attempts and only catches integrity violations — any other database error is re-thrown rather than swallowed [src: app/controllers/app.php:97].
One writer, so a ticket cannot be malformed
Token issuing happens in a single function, and two things are minted there for a stated reason.
The visitor's follow link is created in the one writer so a ticket cannot exist without one — because "a ticket with no link is a ticket nobody can follow, and the kiosk would have nothing to print" [src: app/controllers/app.php:82].
The phone number is normalised there too, so a number that cannot be dialled is stored as nothing rather than as junk the sender would trip over later [src: app/controllers/app.php:88].
Both are the same idea: fix it once, at the only place a row is created.
Calling the next token
First in, first out. The earliest issued token wins, with ties broken by issue order [src: app/src/Queue.php:96].
A counter bound to a service only draws from that service's queue; a general-purpose counter takes the single oldest waiter across all services [src: app/src/Queue.php:97].
"N people ahead of you" uses the same ordering
The figure shown on a visitor's ticket page counts the waiting tokens ahead of theirs in exactly the order the caller uses [src: app/src/Queue.php:121].
That consistency is the point. A queue product whose displayed position disagreed with its own call order would be worse than having no figure at all.
Status, and what a recall is not
Four statuses, with waiting the issued default and served terminal [src: app/src/Queue.php:21].
The legal transitions [src: app/src/Queue.php:25]:
| From | May move to |
|---|---|
waiting | called |
called | served, no_show, or back to waiting — returning a mis-call to the queue |
no_show | called, or waiting — recall an absentee directly, or requeue them |
served | nothing — terminal |
A recall is not a transition. Re-announcing a token that is already called leaves the status unchanged and only refreshes the call time [src: app/src/Queue.php:30].
That distinction matters for your statistics: shouting a number again does not create a second call event.
Waiting times
Wait is measured from issue to call [src: app/src/Queue.php:142]. It returns nothing when either timestamp is missing or unparseable, and zero rather than a negative number if a call somehow precedes an issue [src: app/src/Queue.php:143].
Averages and per-day statistics are computed from those [src: app/src/Queue.php:161], [src: app/src/Queue.php:207].
The public kiosk
The kiosk is rate-limited per address — fifteen issues in five minutes by default [src: app/controllers/app.php:105] — and carries a honeypot field [src: app/src/Queue.php:258].
Users and roles
Tokora's viewer role is granted *.view together with audit.view [src: app/src/Auth.php:27], so a viewer can read the audit trail — and because audit.view is named in the list rather than left to the wildcard, that is a deliberate decision rather than a side effect.
The API and agent access
Six tools are exposed to an agent, of which two write [src: app/controllers/v3.php:175]: listing services, queue status, a ticket's status and daily statistics on the read side; issuing a ticket and calling the next one on the write side.
The agent endpoint reports the same version as the rest of the product [src: app/controllers/v3.php:174].
An agent issuing a ticket goes through the same single writer a kiosk does, so it cannot produce a ticket without a follow link or with an undiallable number [src: app/controllers/app.php:82].
What Tokora does not do
It does not handle money [src: app/src/Queue.php:12]. There is no monetary field in the product at all.
It does not need a cron job [src: app/src/Queue.php:82]. The daily reset is a consequence of how numbers are scoped, not a scheduled task.
It does not let two kiosks issue the same number [src: app/controllers/app.php:65].
It does not count a recall as a new call [src: app/src/Queue.php:30].
It does not report a negative wait [src: app/src/Queue.php:143].