Skip to content

Embedding the server

veriCue works by running the veriCue Runtime - the VeriCueServer class from libvericue-server - inside the application under test. Embedding puts it there on your terms: you link the library and start it yourself in main(), on the transport that suits your setup. This page covers wiring it into your build, configuring authentication and licensing, and - importantly - keeping it out of your production binaries.

Do you need to touch the build at all?

On Linux, vericue-inject starts the server inside an unmodified dynamically linked Qt application via LD_PRELOAD, with the same feature set, authentication and licensing as embedding. It is a supported path on the configurations listed there.

Embed instead when you need a platform injection does not cover (Windows, macOS), a statically linked Qt, a hardened process, or a server you can compile out of release builds and start on your own terms.

With the veriCue SDK installed (see Installation), add to your CMakeLists.txt:

cmake
find_package(vericue REQUIRED)

target_link_libraries(my-app PRIVATE vericue::vericue-server)

If CMake can't find the package, point it at the install prefix:

bash
cmake -B build -DCMAKE_PREFIX_PATH="/usr/local;/path/to/Qt/6.7/gcc_64"

2. Start the Runtime

Create a VeriCueServer after your main window exists and start it. On Linux and macOS, startLocal() is the recommended call for same-host runs:

cpp
#include <vericue/server.h>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QMainWindow window;
    window.setObjectName("MainWindow");
    // ... build your UI ...

    vericue::VeriCueServer server(&window);   // parented - cleaned up with the window
    if (!server.startLocal()) {
        qWarning("veriCue server failed to start");
    } else {
        qInfo() << "VERICUE_ENDPOINT=" << server.localEndpoint();
    }

    window.show();
    return app.exec();
}

startLocal() uses the local IPC transport: a user-private endpoint that is not reachable over the network. It is supported on Linux and macOS. On Windows, and whenever the client runs on another host, in another container, or on a device, start the TCP transport instead:

cpp
    if (!server.start(4242)) {                // reachable from other hosts
        qWarning("veriCue server failed to start");
    }

A portable main() can pick per platform, since the rest of the protocol, the authentication and the licensing are identical either way:

cpp
#ifdef Q_OS_WIN
    server.start(4242);        // Windows: TCP is the supported transport
#else
    server.startLocal();       // Linux/macOS: local IPC when tests run here
#endif

Notes:

  • startLocal() with no argument picks $XDG_RUNTIME_DIR/vericue/vericue-<pid>.sock; read the resolved path back with server.localEndpoint() and print it so the harness can discover it (the injector prints VERICUE_ENDPOINT=<path>).
  • start(0) lets the OS pick a free port; read it back with server.serverPort(). Useful for parallel CI jobs - print it to stdout so the test harness can discover it (the bundled test app prints VERICUE_PORT=<port>).
  • Both at once is allowed: calling startLocal() and start(port) on the same server serves local and remote clients simultaneously. They share the one licensed concurrent-session budget, and stop() shuts both down.
  • The constructor takes an optional QObject* parent. Parenting to your main window ties the server's lifetime to the UI.
  • started() / stopped() / errorOccurred(QString) signals are available if you want to log or react to lifecycle events.
  • Works for QWidget and QML applications alike. Toolkit support is not linked into the Runtime: vericue-widgets and vericue-quick are loaded at run time from the directory holding the Runtime library, and only when the matching Qt module is already in your process. Nothing to configure - just deploy them next to libvericue-server (bin/ on Windows). Confirm with the version command, which reports one status line per toolkit.

3. Name your objects

Clients address objects by path, e.g. MainWindow/centralWidget/okButton. Path segments use QObject::objectName(), falling back to ClassName#N for unnamed objects. Unnamed-object paths break when the UI changes, so:

cpp
okButton->setObjectName("okButton");

Give stable objectNames to every widget you intend to automate. In QML, set the objectName property.

4. Authentication

cpp
server.setAuthToken(qEnvironmentVariable("MYAPP_VERICUE_TOKEN"));

When a token is set, clients must present it during the handshake; connections with a missing or wrong token are rejected. Read the token from the environment or a config file - don't hardcode secrets.

5. Licensing

Without any license configuration the server runs in 30-day trial mode. For licensed use, pick one:

cpp
// Organization license: RSA-signed JSON key file (from your license email / portal)
server.setLicenseFile("/etc/myapp/vericue-license.json");

// Floating: lease a session from your self-hosted LAN license server
server.setLicenseServer("licenses.internal.example.com", 5252);

Licensing is fully offline - the server never phones home. Session accounting differs by mode: with an organization key file (or trial), each connected client occupies one concurrent automation session while connected; with a floating license, the application process checks out one lease from the pool at the first start()/startLocal() and holds it (with heartbeats) until it exits. Starting both transports still checks out exactly one lease. When a license expires (trial or paid), the server keeps answering handshake, ping and version but refuses everything else. See Trial & paid tiers.

6. Compile it out of release builds

The veriCue Runtime is a test-time tool. Don't ship it in production binaries: whichever transport you start, it opens a full control channel into your application's internals. The recommended pattern is a dedicated build option:

cmake
option(ENABLE_VERICUE "Embed the veriCue automation Runtime" OFF)

if(ENABLE_VERICUE)
    find_package(vericue REQUIRED)
    target_link_libraries(my-app PRIVATE vericue::vericue-server)
    target_compile_definitions(my-app PRIVATE MYAPP_WITH_VERICUE)
endif()
cpp
#ifdef MYAPP_WITH_VERICUE
#include <vericue/server.h>
#endif

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QMainWindow window;
    // ...

#ifdef MYAPP_WITH_VERICUE
    vericue::VeriCueServer server(&window);
    server.setAuthToken(qEnvironmentVariable("MYAPP_VERICUE_TOKEN"));
#  ifdef Q_OS_WIN
    server.start(4242);                       // Windows: TCP
#  else
    server.startLocal();                      // Linux/macOS: local IPC
    qInfo().noquote() << "VERICUE_ENDPOINT=" << server.localEndpoint();
#  endif
#endif

    window.show();
    return app.exec();
}

Your QA/CI builds configure with -DENABLE_VERICUE=ON; release builds don't reference veriCue at all - no linked library, no endpoint, no open port, nothing to audit in the shipped artifact.

Security model

Be deliberate about where the automation server is reachable:

  • A connected client can read widget contents, click anything, invoke methods and take screenshots - full control of the UI.
  • startLocal() is local by construction: a UNIX-domain socket with owner-only permissions, no port, no network presence. Prefer it whenever the client runs on the same machine.
  • start(port) listens on all network interfaces - there is currently no bind-address option. On a developer machine or CI runner that's usually fine; on a shared network it means anyone who can reach the port can drive your application.
  • The TCP transport is plaintext (length-prefixed JSON). There is no TLS. Treat the port like a local debug interface, not a network service.

Recommendations, in order of importance:

  1. Only embed in test builds (compile-out pattern above). The strongest control is the server not existing.
  2. Use local IPC unless you actually need cross-host access - see Transports. On Windows local IPC is not supported yet, so a Windows rig runs on TCP and depends more heavily on the token and the firewall.
  3. Always set an auth token outside single-developer machines.
  4. Firewall the port to localhost or the CI subnet.
  5. For remote debugging across machines, tunnel instead of exposing: ssh -L 4242:localhost:4242 testrig and connect to localhost.
  6. Never expose the port on an untrusted or public network.

Released under a commercial licence. Privacy · Terms