How Telos is built, and how we check it
The architecture, the correctness rules and the security settings, with the numbers we counted.
Two ways to run, one set of rules
Telos is a desktop application. It runs in two shapes, and a third site is the vendor's account portal.
- Standalone, one PC. The app holds the business rules itself and keeps its data in a local database encrypted with SQLCipher (AES-256). There is no server and no internet requirement.
- LAN server, several stations. The same desktop app talks over HTTPS to a server in your shop. The server is a Spring Boot application (Java 21) with PostgreSQL, Redis and MinIO for files, behind an nginx proxy, installed with Docker Compose on Ubuntu Server LTS.
- Account portal. The website you are on handles sign-up, licences, billing, downloads, AI credits and cloud backup. It does not run your business records.
The two runtimes are written separately, Dart for standalone and Java for the server, and that is on purpose: a one-PC customer needs no Java and no server process.
Platforms and hosting
- Desktop app for Windows 10 and later (x64) and macOS 12 and later (Apple Silicon and Intel), and a Linux build for glibc 2.31 and later
- LAN server on Ubuntu Server LTS with Docker; recommended hardware is 4 or more cores, 16 GB of memory, a 256 GB or larger SSD, a fixed LAN address and a UPS
- Install and update from the vendor's laptop over SSH in one command; a backup is taken before every update, and a self-signed certificate is created with your server's address, or you can supply your own
- Desktops find the server on the network by themselves; the installer sets the server's firewall to answer only your private network
- A nightly backup job (02:00, 14 days kept by default) writes a database and file snapshot with a SHA-256 checksum, and a restore script can run a restore drill. Check on your own server that it ran
One rule, two implementations, one test suite
Because the rules exist twice, they could drift apart. We prevent that with test vectors: files of inputs and the exact outputs each rule must produce. Both the Dart code and the Java code are run against the same files, and the results must match byte for byte. A change to a rule ships with its Dart code, its Java code and its vector in the same change.
What we counted in the repository on 29 October 2026:
- 196 vector files in 23 families, found by listing the
packages/test-vectorsfolder. They cover stock allocation by expiry (FEFO), money and rounding, the Solar Hijri calendar, pricing, sales totals, the audit chain, the password policy and more. - 258 permissions and 19 system roles across 23 modules, defined once in a single file and generated into both runtimes.
- 15,880 message keys in each of Dari, Pashto and English, across 84 files, with key-for-key parity checked by a script.
- 214 PostgreSQL migrations and 194 SQLite migrations, and a script that checks the two schemas are equivalent.
These are counts of what exists, not results. We do not publish a pass rate. The suites are run on both runtimes as part of release readiness.
Correctness rules built in
- Rounding is banker's rounding (half to even) everywhere, in both runtimes
- Every amount is stored together with its currency; there are no bare money columns
- A foreign-currency document keeps the exchange rate it was made with; the rate is never recomputed
- An invoice counts as paid when the balance is within half a display unit, the same test in both runtimes
- Document numbers come from a counter table inside the same transaction, never a database sequence, so a cancelled save leaves no gap
- Stock is only ever changed through the inventory module, so every movement has one source
Audit log and retry safety
Audit log. Every change to your records writes an audit row inside the same database transaction. The database itself rejects any update or delete on the audit table, in both PostgreSQL and SQLite. Each row stores sha256(previous hash || canonical row), so removing or editing a row breaks every row after it. The chaining rule is pinned by a shared test vector, and a scheduled job on the server verifies the chain.
Idempotent commands. Every command that creates a document must carry an idempotency key. If the same request arrives again, for example after a dropped connection, the server returns the original response for 24 hours instead of posting again. The same key with a different body is refused with a conflict error.
Record Trash. An Admin or Owner can move finance, purchase and sales documents out of daily lists, with a reason and reversibly. It hides them from reports too and shows a banner with how many are hidden. Nothing is deleted.
Security settings, exactly
- Passwords: 12 to 256 characters with upper case, lower case, a digit and a symbol. Common passwords, four identical characters in a row and passwords built from the person's e-mail name or display name are refused. The rule is the same in both runtimes and is pinned by a vector of 13 cases. Passwords are hashed with Argon2id.
- Two-step sign-in (MFA): a TOTP authenticator app plus single-use recovery codes, which are stored hashed. An Admin can require MFA for a user, and that user cannot sign in until they have enrolled.
- Tokens and sessions: access tokens last 15 minutes, refresh tokens 7 days. Sessions are kept on the server in Redis and can be revoked at once.
- Lockout: 10 failed sign-ins within 15 minutes lock the account.
- Permissions: 258 permissions grouped into 19 system roles, with limits by branch and warehouse enforced at the data layer. A person's rights are the union of their roles.
- Licences: signed with Ed25519, tied to the device, with a seat limit per package. A cached licence works offline for 14 days by default.
This page describes what the code does. We do not list certifications, outside penetration tests or uptime figures, because we have none to show. See also Security.
Built for three languages from the start
- Every screen string lives in one master file per language and is generated into the Java, Flutter and website formats, so a missing translation is caught by a key-parity check.
- Dari and Pashto are written in the Afghan administrative register used on invoices and bank forms, following a style guide and a fixed glossary, not in Iranian Persian.
- The layout is fully right-to-left, with Latin digits throughout.
- The Solar Hijri calendar is converted in one module per runtime, and both are held to the same test vectors.
- Invoices and other documents are printed as A4 PDF, and receipts go to thermal printers through the ESC/POS protocol on 58 mm and 80 mm paper. Label printers use ZPL and TSPL. We support the protocols; we do not certify specific printer brands.