Abhard Configuration Reference
Settings live in abhard.yaml. A configuration the service creates for itself
holds no devices, and its whitelist is widened to the /24 of whichever network
card the service could identify.
An example covering every supported device ships with the program —
/usr/share/doc/abhard/examples/abhard.yaml.example on Linux,
C:\Program Files\Abacus\Tools\examples\abhard.yaml.example on Windows.
The main block
| Key | Default | Description |
|---|---|---|
port |
4601 |
Port the service listens on |
whitelist |
127.0.0.1 |
Allowed addresses, semicolon-separated. Accepts single addresses (127.0.0.1), ranges (172.27.0.2-10) and subnets (192.168.50.0/24) |
event_socket |
true |
Enable the event channel |
event_socket_host |
detected automatically | The address the service hands to cash desks as the event-channel endpoint |
event_socket_port |
4651 |
Event channel port |
event_socket_ping_idle |
4 |
Seconds of silence after which the cash desk is pinged |
language |
uk |
Language of the device states and errors users see. The translation is read from lang/abhard.<language>.po next to the program; an empty value means English |
Log settings live in a separate log block - see Logs.
The event channel
The event channel is a permanent TCP connection over which the service pushes barcode scans and other service information to the cash desk on its own. The program that connected last gets the scanned codes. The connection is made when a program starts and when each program in the "Abacus" suite (Cashdesk, Sklad, Service, Barcom) is activated. It disconnects only when the program closes. So if you simply minimized the program, or opened another program that is not part of "Abacus" (a browser, a text editor, etc.), the program keeps receiving scanned codes. This is very convenient when you need to work in Cashdesk or Sklad and in a supplier's price list at the same time.
If the active program does not answer a ping within two seconds, it is considered gone and is disconnected. If another program was running at that point, it is not activated.
This is done specifically so that a frozen Cashdesk does not block the channel, while at the same time codes do not suddenly start being sent to a long-forgotten Sklad that is still running.
The service listens on every network card but advertises exactly
event_socket_host to the cash desk. Automatic detection takes the first
192.168.* address, then any non-local one, and only then 127.0.0.1. On a
machine with several network cards it can pick the wrong one: the cash desks
then receive an unusable address and never connect — set it explicitly in that
case.
Devices
devices is a sequence; each entry describes one device.
| Key | Required | Description |
|---|---|---|
name |
Yes | Device name; the cash desk and the log refer to the device by it. An entry without a name is skipped |
type |
Yes | scaner, printer, scales, prro |
subtype |
Yes | See below; not needed for scales |
The remaining keys depend on the type/subtype pair. Valid pairs:
scaner/serial, scaner/hid, scaner/devfile, printer/escpos,
printer/dummy, scales, prro/eusign, plus any type with
subtype: redirect. An unknown pair is skipped with a warning in the log — the
service starts, the device simply is not there.
The scanner type is spelled
scaner, with onen.scanneris an unknown type.
Shared RS-232 parameters
Apply to scaner/serial and to scales.
| Key | Default | Description |
|---|---|---|
device |
— | Serial port: /dev/ttyS0, COM1 |
baudrate |
9600 |
Baud rate |
bits |
8 |
Data bits |
parity |
N |
N · E · O · S · M |
stopbits |
1 |
Stop bits |
softflow |
false |
XON/XOFF flow control |
hardflow |
false |
RTS/CTS flow control |
scaner/serial
Barcode scanner on a serial port. It takes no parameters beyond the RS-232 ones. While the device file is missing the scanner reports "Not found" and the service retries every second — a scanner unplugged and plugged back in recovers on its own.
scaner/hid — Linux only
USB scanner driven directly through libusb.
| Key | Default | Description |
|---|---|---|
vendor_id |
— | Vendor code, hexadecimal, no 0x prefix: 0536 |
product_id |
— | Model code, same form: 01b3 |
lsusb prints both.
scaner/devfile — Linux only
USB scanner read through a device file.
| Key | Default | Description |
|---|---|---|
device |
— | Device file path: /dev/hidraw0 |
printer/escpos
ESC/POS receipt printer.
| Key | Default | Description |
|---|---|---|
device |
— | Device file: /dev/usb/lp0, COM2 |
width |
40 |
Characters per line; long lines wrap and separator rules are drawn to this width |
codepage |
(none) | Printer codepage number. If unset, the printer keeps whatever it is currently set to |
softrender |
false |
true — the service draws the QR code and sends it as an image; needed for printers with no QR command of their own |
feed_lines |
3 |
Lines to feed before the receipt is cut |
printer/dummy
Saves receipts as text files instead of printing them. Useful for checking the cash desk configuration where there is no printer yet.
| Key | Default | Description |
|---|---|---|
target_dir |
(none) | Where to put the files. If unset, the receipt goes to the log |
width |
40 |
Characters per line |
scales
Scales on a serial port. They need no subtype.
| Key | Default | Description |
|---|---|---|
model |
VTA16 |
VTA16 or DigiDS |
timeout |
5000 |
Milliseconds to wait for the scales before returning an error |
Plus the RS-232 parameters; device defaults to /dev/ttyS0 for scales.
prro/eusign
Ukrainian software fiscal register. What the cash desk does with it is covered in the PRRO section.
| Key | Default | Description |
|---|---|---|
key_path |
— | Private key file |
key_password |
— | Key passphrase |
certs_path |
program directory | Directory with certificates. The service creates it if missing and fills it itself — see below |
lib_path |
/opt/abacus/lib/ |
Signing library directory. If it does not exist, the lib/ subdirectory of the program directory |
proxy_address |
(none) | Proxy server through which the tax service is reachable |
proxy_port |
0 |
Proxy port |
proxy_user |
(none) | Proxy user |
proxy_pass |
(none) | Proxy password |
timeout_ms |
30000 |
Milliseconds to wait for the tax service |
http_retries |
2 |
How many times to repeat a request that did not get through |
http_retry_delay_ms |
1000 |
Pause between retries |
A path that does not start at the root or a drive letter is resolved against the program directory.
Proxy. The service passes the same
proxy_address/proxy_portto two different clients: its own, for the tax service, and the signing library, for the providers' servers. The first expects SOCKS, the second HTTP. So a single entry must point at a proxy that understands both protocols; otherwise only one of the two directions works. If you don't need a proxy, don't set it at all: an empty value explicitly turns it off in the library's settings, so an old value doesn't stay in effect.
Which certificates go into certs_path
None. The key file and its password are enough.
The service creates the directory itself if it is missing, and downloads everything it needs from the provider's server on first start. Verified with keys from three different providers: empty directory, and in 7-50 seconds the key is read and the device works.
- name: 'main_prro'
type: 'prro'
subtype: 'eusign'
key_path: 'keys/pb_0123456789.jks'
key_password: 'Password1234'
certs_path: 'cert/' # the directory need not exist yet
How it works: if the key fails to read, the service takes the provider
directory (CAs.json) and queries the CMP servers of every accredited provider
until one of them returns certificates for this specific container. The
certificates received are stored in certs_path, and reading the key is
retried. If that still doesn't help, the shared CACertificates.p7b bundle is
downloaded and reading is retried once more.
This happens only when the key fails to read. A working device with a filled directory never touches the network at all.
For this to work the service must be able to reach the providers' servers
(iit.com.ua and the provider's own site) over plain HTTP. The certs_path
directory must be writable by the service.
This section used to require placing three files by hand. That is no longer needed, but it doesn't hurt either: if the certificates are already in the directory, the service downloads nothing.
If the key still doesn't read
Steps in order, cheapest first.
1. Check the password. Error 24 or 36 is a password or a corrupted key
file. Certificates have nothing to do with it, and the service doesn't even try
to download anything: the log shows Not repairing the store ... the key
container itself did not open.
2. Check the age of the signing library. This is the most common cause of
error 50 in 2026. Providers switched to a new signature algorithm (DSTU
7564), and a 2020-vintage library doesn't know it — and reports this not as
"unsupported algorithm" but as an ordinary "certificate is corrupted". The
tell: keys from some providers work while freshly issued keys from others
don't.
0 means the library is old and needs updating; any other number means the
library isn't the issue. How to update it is covered in the technical docs
(daemons/docs/eusign_library.md, "Updating the library" section).
3. Check network access. Could not fetch certificates ... error 5 in the
log means the CMP servers are unreachable: no internet, or a proxy is set that
the library doesn't understand (see the proxy note above).
Checking the owner's certificate
Owner, issuer and validity — for when you need to know whose certificate this is and whether it is still valid:
$c = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 'C:\Abacus\Tools\cert\FILE.crt'
$c.Subject; $c.Issuer; $c.NotBefore; $c.NotAfter
To look inside a bundle:
What the log shows during PRRO startup
The lines appear in this exact order. A successful device startup is about a dozen lines; if there are more, look for an error among them.
| Log line | What has already happened |
|---|---|
Created cert store <path> for <name> |
the certificate directory didn't exist, the service created it |
PRRO <name> using key <path> (<N> bytes), certs <path>, lib <path> |
paths parsed; shows exactly which key file was picked |
EUSign proxy set to <address>:<port> ... |
the library's proxy is configured; the line is absent if no proxy is set |
EUSign for <name>: offline <on/off>, check CRLs <on/off>, auto download CRLs <on/off>, OCSP <on/off>, CMP <on/off> |
the library's mode of operation; OCSP and CMP server addresses are shown only when they are enabled |
Cert store <path>: <N> files, library accepted <M> end-user certificates |
two different numbers: files in the directory, and end-user certificates the library accepted. They don't match — CA and server certificates are counted separately, and one .p7b bundle holds many certificates |
Key container <path> opens on its own, <N> bytes of key info |
the container is intact and the password is correct |
Reading the private key for <name> |
reading the key together with the certificates. If the log stops right here, the library has hung on this call, not the service crashing |
Key certificate <N>: serial <number>, issued by "<issuer>" |
certificates the library matched to the key; use the issuer to find the provider |
Key loaded for <name>: <owner>, expires <date>, earliest of <N> own certificates |
key read successfully; the date is the earliest expiry among its certificates |
Device <name> initialized successfully. |
the device is ready |
If the key fails to read, instead of the last two lines there is an error, followed by a list of every end-user certificate the library can see:
| Log line | What it means |
|---|---|
ReadPrivateKeyFile failed for <name>, error <code>: <description> |
the error code from the library; the most important ones are below |
Store certificate <N>: subject "<owner>", serial <number>, issued by "<issuer>" |
what's actually in the store; look here for the key's owner |
Not repairing the store for <name>: the key container itself did not open |
wrong password or corrupted key file — there's no point downloading certificates |
Fetching CAs.json for <name> from <address> |
the provider directory is being downloaded |
Skipping <N> unreachable CMP servers for <name> |
this many provider servers didn't answer the connection; the rest will be queried |
Fetching certificates for <name> from <N> CMP servers |
querying provider servers; takes up to half a minute |
Installed <N> bytes of certificates for <name> from the issuer |
certificates received and saved to certs_path |
Retrying key read for <name> after repair step <N> |
retrying the key read: 1 after the provider's certificates, 2 after the shared bundle |
Could not fetch certificates for <name> from the issuer, error 5 |
the provider servers are unreachable — network or proxy |
Not fetching certificates for <name>: EUSign is offline |
certificates were not downloaded because the library is running offline |
Device <name> is still initialising after 30 s and is not usable |
the library never returned control; the device is marked failed, the service keeps running |
Two error codes come up most often, and they must not be confused:
| Code | Description from the library | What to do |
|---|---|---|
51 |
Certificate not found |
a directory problem, not a key problem: the issuer chain doesn't resolve. The service usually fixes this on its own; if not, the provider servers are unreachable |
50 |
Certificate is corrupted or cannot be used |
the certificate was found but is rejected. The most common cause in 2026 is an old signing library that doesn't know the provider's new algorithm; see step 2 above. Less often, the validity period has expired or the certificate was issued for a different key |
24, 36 |
invalid password or corrupted key |
password or key file. Certificates are irrelevant |
The word "certificate" in both descriptions refers to the certificate, not the key file — yet the service's own message says "Failed to read private key", and because of this a working container has been swapped out for nothing more than once. Two keys long believed corrupted turned out to be entirely fine: they just needed a fresh library.
subtype: redirect
A device physically attached to another cash desk — scanner, printer, scales or PRRO. Instead of working with hardware the service returns the address of that abhard, and the request is carried out there.
| Key | Default | Description |
|---|---|---|
redirect_url |
— | Base address of the remote abhard |
redirect_name |
— | Name of the device on it |
redirect_token |
— | Token code of the remote abhard. Without it the request goes unauthenticated |
subtype: redirect overrides type, so the rest of that type's parameters have
no effect here. What this requires on the Launcher side is covered in the
Launcher section.
Access tokens
Neither the installer nor the administrator creates the abhard.json token
file. The service starts out unclaimed, and the first admin client takes it
over: activating the services in the Sklad configuration editor (or initialising
from the Launcher) receives an administrator token from the service, then issues
one token per role and stores them in the database under mnemonic codes. No key
is ever copied by hand.
Sklad lists every issued token with its code: the employee reference, "Токени"
tab. That is where the code for redirect_token comes from.
A service can only be claimed while the token file is absent. After that a previously issued administrator token is required, and a repeated initialisation answers "already initialised".
| Role | Permissions |
|---|---|
administrator |
Everything, including issuing and revoking tokens |
worker |
All hardware operations; cannot issue or revoke tokens |
probationer |
Same as worker, but cannot produce a Z-report |
A token with no expiry date never expires. An expired token is rejected and removed from the file at once.
Example
main:
port: 4601
whitelist: '127.0.0.1;192.168.50.0/24;172.27.0.2-10'
event_socket: true
event_socket_host: '192.168.50.10'
event_socket_port: 4651
event_socket_ping_idle: 4
log:
file: 'abhard.log'
level: 3
maxsizekb: 10240
keepfiles: 10
devices:
- name: 'main_scaner'
type: 'scaner'
subtype: 'serial'
device: '/dev/ttyS0'
baudrate: 9600
- name: 'usb_scaner'
type: 'scaner'
subtype: 'hid'
vendor_id: '0536'
product_id: '01b3'
- name: 'main_printer'
type: 'printer'
subtype: 'escpos'
device: '/dev/usb/lp0'
width: 42
feed_lines: 3
- name: 'shared_printer'
type: 'printer'
subtype: 'redirect'
redirect_url: 'http://192.168.50.4:4601'
redirect_name: 'main_printer'
redirect_token: 'abhard_<neighbour profile>_worker'
- name: 'main_scales'
type: 'scales'
device: '/dev/ttyS1'
model: 'DigiDS'
timeout: 5000
- name: 'main_prro'
type: 'prro'
subtype: 'eusign'
key_path: 'keys/pb_0123456789.jks'
key_password: 'Password1234'
certs_path: 'cert/'
The cert/ directory need not exist yet — the service creates and fills it
itself.
Checking it works
$KEY is any valid token of the service.
One record per device: name, type, state and last error. A device missing from
the answer was never created — look for the warning in the log. /api/list
returns every available request.
PRRO has a connectivity check of its own:
# check now (the numbers in the answer are per-stage timings)
curl -H "X-API-KEY: $KEY" http://192.168.50.10:4601/api/prro/main_prro/selftest/run
# recent document submission attempts
curl -H "X-API-KEY: $KEY" "http://192.168.50.10:4601/api/prro/main_prro/history?limit=20"
After three consecutive failures the service runs that check by itself; the
result stays available at /api/prro/<name>/selftest.
A 401 means a missing or invalid token; a 403 means an address absent from
the whitelist, or an action this token's role may not perform.