Skip to content

ACE — Automation & Control Engine: Comprehensive FAQ

Comprehensive Q&A — covering getting started, architecture, components, the Command/Job system, the Node Editor, privilege management, scripting, webhooks, hardware, chat, security, editions, deployment, troubleshooting, and practical how-to topics.


Table of Contents

  1. Getting Started
  2. General & Architecture
  3. Environments
  4. Users & Authentication
  5. Databases
  6. Commands & Jobs
  7. Privilege System
  8. Permission Groups
  9. Timers
  10. Variables & Expressions
  11. Devices, Controllers & Sensors
  12. Files, Webhooks & Processes
  13. Node Editor & Scripting
  14. Chat System
  15. Security & Encryption
  16. Networking Internals
  17. Editions & Pricing
  18. Deployment & Installation
  19. Scripting Engine (.asf Files)
  20. Polls & Voting
  21. System Commands & Process Management
  22. Serial Ports (Advanced)
  23. Update System & Multi-Language
  24. Use Cases
  25. Troubleshooting
  26. GUI Controls

1. Getting Started


Q. I just downloaded ACE Installer. What are the first steps?

A. 1. Start the ACE Installer 2. Choose whether you want to install Server, Client or Embedded (uploading to Arduino/RaspberryPi) 3. Follow the on-screen installation instructions

Q. Can the Raspberry Pi Embedded CLI Installer reboot automatically after installation?

A. Yes. If UART activation needs a reboot, the CLI Installer normally asks Do you want to reboot the Raspberry Pi now? [y/N]: after a successful installation; Enter defaults to No. For unattended or bulk Raspberry Pi deployment, start the Installer with --reboot-after-install (short form -rai). ACE then reboots only after the installation has completed successfully and only when that reboot is actually required. The option does not make failed installs, Arduino installs, Server installs or Client installs reboot.

After a successful installation, start the launcher for GUI (graphical user interface) or CLI (command line interface) version of ACE.


Q. How do I access a CLI ACE service that starts automatically on Linux?

A. The Linux CLI Installer can manage CLI services through GNU Screen. It is offered as a recommended [Y/n] step for CLI Server, Raspberry Pi Embedded, and a Client for which you choose systemd service setup. If Screen is not already installed, the Installer can install it with a supported system package manager.

To open the running console later:

sudo screen -r ACE_Server
sudo screen -r ACE_Client
sudo screen -r ACE_Embedded

Use the command matching the component you installed. To leave the console without stopping ACE, press Ctrl+A, then D. To list sessions, run sudo screen -ls.

Screen detaches you from the console; it does not replace service control. To stop or inspect a service, use systemd, for example:

sudo systemctl stop ACE_Server.service
sudo systemctl status ACE_Server.service

Q. Do I need programming knowledge to use ACE?

A. No. ACE is designed for both technical and non-technical users:

  • No-code — the Node Editor lets you build automation visually by dragging and connecting nodes. No CLI knowledge required.
  • Low-code — the Command Builder wizard guides you through every command with labelled input fields and live suggestions from your environment.
  • Power users — the full CLI with 450+ commands is available for scripting, batch operations, and advanced workflows.

Most home automation and industrial control tasks can be completed entirely within the GUI without writing a single line of code.


Q. What is the fastest way to automate something without hardware?

A. Pure software automation needs only a Command and a Timer — no serial port, no controller:

  1. Create a Command and add jobs to it. You can build the jobs via the Node Editor + Command Builder

  2. Create a Timer and set its:

  3. interval
  4. number of repeats (-1 for infinite repeats)
  5. attach Command to the Timer

  6. Start the timer or schedule the start (specific time and date)

Q. How do I connect the Client to a Server on a different machine?

A. Start the launcher of ACE Client (either GUI or CLI version)

Enter: - Address — the server machine's IP address or hostname (e.g., 192.168.1.42) - Port — default is 4542 (configurable via system set-server-port) - Username / Password — your account credentials in the target environment

All connections are encrypted via TLS. For access from outside your local network, see the Deployment & Installation section.


Q. Can a Linux Client start on boot and log in on its own?

A. Yes. During a Linux CLI Client installation, the Installer can optionally create ACE_Client.service. The service setup is opt-in and the Installer asks whether you want:

  • a systemd service;
  • automatic login;
  • the target environment when auto-login is selected;
  • automatic process restart after a real crash (recommended for unattended clients);
  • GNU Screen management (recommended).

Auto-login keeps retrying when the Server is temporarily unavailable and reconnects after a network drop. A normal Client exit is respected, and an ACE update or intentional restart is not treated as a crash.

If you do not enable auto-login, Screen is required for an interactive Client service so you can attach and enter the login information.

You can rerun the Linux CLI Client Installer later to change the Server connection, login details, auto-login environment, restart behavior, or Screen setting. If Client startup was configured previously, the Installer lets you reconfigure it or remove it. If auto-login is turned off, the Installer will not silently keep the previous unattended-login setup.

On Raspberry Pi, the existing in-client autostart controls are also available after login:

client autostart status
client autostart disable
client autostart enable <password>

The GUI login window also offers "Start on boot and log in automatically" where that Raspberry Pi autostart feature is supported.

Security: unattended login stores the required sign-in information securely on the device. Enable it only on a machine you trust.


Q. Can a Client register the first admin user for a new environment?

A. No. First-time registration is server-side only (Server GUI/CLI). Every environment must have its initial admin user created from the Server path.

If a client tries to connect to an environment that has no users yet, authentication cannot succeed (there is no account to validate), so login fails until the server-side registration is completed.

Q. I forgot my password. How do I regain access?

A. If another admin user exists: - Admin user can change any user's password - Privileged users can change user's password

There's no other way to regain access.


Q. How do I see all available commands in ACE?

A.

help                      # lists all available commands

In the GUI, the Command Builder inside the Node Editor lists every command in a searchable, categorized palette with labelled parameter fields and inline tooltips.


Q. Does ACE have a mobile app?

A. There is no dedicated mobile app. The built-in web dashboard (default port 4541) works in any mobile browser and provides real-time sensor & variable monitoring, command execution, and chat. For full management (privilege editing, hardware setup, scripting), use the desktop GUI or CLI.


2. General & Architecture


Q. What is ACE and what problem does it solve?

A. ACE (Automation & Control Engine) is a secure, self-hosted, high-performance automation platform written in modern C++. It bridges software automation (scripts, processes, files, OS commands) and physical hardware control (sensors, relays, LEDs, RFID readers, Arduino, Raspberry Pi) under a single unified Server/Client architecture.

The core problem ACE solves is fragmentation: most automation tools handle either software tasks or hardware, require cloud subscriptions, or force you to write raw code. ACE provides a no-code/low-code environment that runs entirely on your own machine, keeps all data on-premise, and scales from a single Raspberry Pi to a multi-server enterprise deployment — without any third-party cloud dependency.


Q. What is the difference between the Server and the Client in ACE?

A. ACE follows a centralized Server/Client model.

Server hosts environments, users, databases, timers, variables, commands and other components, hardware abstraction, web dashboard (port 4541), and the Resolver (the command executor). All automation logic executes here. Provides the GUI & CLI. Can control software & hardware, serial ports and GPIO. The server is the single source of truth.

Clients connect to the Server, they send commands to the server. Clients can use GUI & CLI to communicate with the Server. Can control software & hardware, serial ports and GPIO. Multiple clients can be connected simultaneously to the same server.


Q. What platforms does ACE support?

A.

  • Windows — x86_64 (native installer, CLI, GUI)
  • Linux — x86_64 and aarch64/ARM (supports Raspberry Pi hardware directly)

Q. How does ACE communicate between server and client? Is traffic encrypted?

A. All communication between the ACE Server and ACE Clients is encrypted using SSL/TLS.


Q. What is the Resolver?

A. The Resolver is ACE's command executor — the engine that executes every ACE command. It lives entirely on the server. When a client types or sends a command (e.g., command execute alice main MyCommand), the Resolver parses it, validates permissions, locates the target objects, and executes the appropriate action.


Q. How can I reference the current user or environment without hardcoding names?

A. Use ${current_user} for the Current User and ${current_env} for the Current Environment. This makes commands and scripts easier to reuse in different environments.

print ${current_user}@${current_env}

In the Node Editor and Script Builder, you can use the Current User and Current Environment nodes instead of typing the placeholders yourself.


Q. How many commands does ACE support?

A. ACE's Resolver supports 450+ CLI commands.


Q. What is a UID in ACE and how is it structured?

A. Every ACE component has a unique identifier called a UID. The UID encodes the object's hierarchy so the server can look up anything by its UID.


Q. Does ACE have a web dashboard?

A. Yes. The ACE Server includes a built-in web dashboard served on port 4541. The web dashboard provides:

  • Real-time component status monitoring (sensors, variables)
  • Remote Command execution
  • Device management (device toggles can use server-side TOGGLE)
  • Chat

Device toggles can use server-side TOGGLE, which switches the current authoritative value between 0 and MaxValue.

The dashboard is designed for monitoring and quick control access. The full management interface (privilege editing, hardware configuration, scripting) is available through the ACE desktop binaries.


Q. Can multiple ACE server instances work together?

A. Yes. ACE supports server-to-server redirection: multiple Local Server instances can redirect messages and requests between each other. This enables multi-server setups.

Each link is one-way — it carries traffic from the server that dialled it to the server that accepted it — so a two-way setup means each server listing the other. See Is a link between two servers two-way? below.

The servers have to be launched on different machines with different ACE Environments.


Q. Is ACE open-source? Does it require a license?

A. ACE uses a commercial licensing model with per-environment activation. Each environment requires a valid license key. Expired environments are locked until reactivated. The source code is maintained in a private repository.


3. Environments


Q. What is an Environment in ACE?

A. An Environment is the top-level isolation boundary in ACE. Think of it as a completely self-contained workspace. Each environment has:

  • Its own users (with their databases, devices, commands, timers, variables, sensors, serial ports, controllers)
  • Its own permission groups and privilege rules
  • Its own backup directory and log directory

Multiple environments can exist on the same server simultaneously. Each environment is completely isolated — a user in environment A cannot access environment B without explicit cross-environment configuration.


Q. What is a Virtual Environment?

A. A Virtual Environment is a temporary, in-memory environment used by the scripting engine to safely test and validate automation logic before deploying it to a production environment. Virtual environments are never persisted to disk.

Scripts run against a virtual clone of your environment first, catching errors without affecting live data.


Q. How do I create an environment?

A. Via the CLI:

env add-environment <license_key> <env_name> <edition_name>

Via the GUI:

New -> New Environment


Q. Can the first user of an environment be registered from the Client?

A. No. The first user in an environment is the initial admin account, and first-time registration must be completed from the ACE Server (GUI or CLI), after accepting the license agreement.

A Client can only authenticate against users that already exist on the Server. If a Client connects to an environment that has no registered users yet, ACE shows a registration-required warning and the login cannot succeed until server-side first-time registration is completed.

On normal startup, a single environment is auto-selected. If you reject the license agreement or cancel registration in the Server GUI, ACE shows environment selection with a quit option before retrying registration, even when only one environment exists.

Q. The Server keeps restarting and never comes up — how do I restore a full backup?

A. Two ways, both landing in the same full-backup chooser:

  • On demand — start the Server with --restore-backup (-rb). It prompts for a backup on that start (a CLI prompt on the CLI binary, a chooser dialog on the GUI one), restores the chosen one and restarts. Use it when the data directory is damaged in a way startup does not detect on its own — a truncated or half-written file can make startup abort without ever raising the decrypt/integrity error that used to be the only trigger for recovery.
  • Automatically — the Server Launcher counts how long each start survives. A start that keeps running for at least 10 seconds is treated as successful and resets the counter; anything shorter increments it. After more than 3 consecutive failed starts, the launcher passes --restore-backup itself, so a crash-looping Server ends up asking which backup to restore instead of restarting forever. That injection is one-shot and resets the counter, so a Server that still fails after a restore gets three fresh attempts rather than an endless loop. Clean shutdowns, update hand-offs and "needs administrator rights" exits are not counted as failures.

Q. How do I back up and restore an environment?

A.

Backup:

env backup

List backups:

env backup-list

Restore:

env backup-load <folder_path>
Loads the backup into the current environment. A restart is recommended after restore.

GUI has its own buttons for creating and loading backups in the Environment's panel/section (after clicking on an Environment inside of the component tree)


Q. What are Environment Events?

A. Environments emit lifecycle events to which you can attach Commands. When the event fires, all attached commands execute automatically.

EventName Trigger
BeforeLoad Before loading a script
AfterLoad After loading a script
BeforeCreateUser Before a new user is created in this environment
AfterCreateUser After a new user is created
BeforeCreatePermGroup Before a permission group is created
AfterCreatePermGroup After a permission group is created
OnReactivation When the environment's license is (re)activated, e.g. after a successful renewal

Attach a command to an event:

env add-event-command <EventName> <username> <database> <command_name>


Q. What is Environment save/access control?

A.

  • Save — controls whether the component's state is persisted to disk (exported). When Save = false, the component exists in memory but changes are never written.
  • Access — controls whether the component is visible to users who have not been explicitly granted a privilege to see it. public makes it visible in listings to all users; private hides it. This is not only about the visibility, but also about access/management of that component.

4. Users & Authentication


Q. What is a User in ACE?

A. A User is an account within a specific environment. Users are the owners of all functional resources: databases, and through databases — commands, timers, variables, devices, sensors, controllers, and serial ports.


Q. How do I create a user?

A.

GUI: - "New" button (top left; above component tree) -> New User CLI: - user create <username> <password> <repeat_password> <admin_status> (admin_status -> 0 / 1)


Q. What are User Events?

A. Users emit connection lifecycle events:

EventName Trigger
OnConnect A client connection is established for this user
OnLogin Successful login
OnDisconnect A client connection drops
OnLogout Explicit logout

Each event can have multiple Commands attached that execute automatically.

user add-event-command <EventName> <username> <database> <command_name>


Q. What is the difference between an admin user and a regular user?

A. An admin user bypasses ACE privilege checks. Admins can perform any operation on any component within their environment without needing explicit privilege grants.

A regular user must be explicitly granted each privilege before they can perform the corresponding action. The full privilege table has 200+ entries covering every operation on every component type.

Use env list admins to see all admin users. The first user registered in a new environment is automatically the admin.


Q. What are User Aliases?

A. User aliases are per-user command shortcuts. When a user types an alias, the server expands it to the full command before passing it to the Resolver.

Aliases live only within the scope of one user and one environment.


5. Databases


Q. What is a Database in ACE?

A. A Database is a named container owned by a User. It holds all the functional components that user has created:

Database
├── Commands
├── Variables
├── Timers
├── Sensors  (generic Sensor, RFIDReader, EventSensor, Button)
├── Devices  (generic, Relay, LED/Light)
├── Controllers  (Arduino, RaspberryPi)
|── SerialPorts
└── ...

Think of a database as a logical workspace for one user. A single user can have multiple databases — for example, home_automation and office_monitoring. Each database is independent: components in one database can reference components in another. Having multiple databases also allows naming the components the same names (because database names differ - the UID becomes different).


Q. How do I create databases?

A.

GUI: - "New" button (top left; above component tree) -> New Database CLI: - database create <username> <database_name>

Q. Can I move components between databases?

A. Yes. Use change-owner (CLI) on the individual component. GUI has "Change Owner" button after clicking a component from the component tree.

This updates the component's internal owner references and updates its UID accordingly. All privilege entries that reference the component are updated to reflect the new UID.


6. Commands & Jobs


Q. What is a Command in ACE?

A. A Command is ACE's fundamental executable unit. It is a named, ordered list of Jobs. When you execute a command, each job runs its assigned task (an instruction string).

Commands are the universal attachment point for automation: - Timers fire Commands on a schedule - Variable Expressions fire Commands when a condition is met - Sensors fire Commands when a reading matches a threshold - Webhooks fire Commands on incoming HTTP POST - Component Events fire Commands on events - Devices can fire Commands on value-change events - Polls fire Commands when a winner is determined

Everything in ACE that wants to "do something" does it by executing a Command.


Q. What is a Job?

A. A Job is a single task inside a Command. Each job has:

  • Name — unique within the command (e.g., TurnOnRelay, LogValue)
  • Task — a Resolver command string that executes when the job runs (e.g., device set-value alice main Relay heater 1)

Jobs run in the order they are stored (if not async). Their order can be changed.


Q. How do I execute a Command?

A. Fire-and-forget (asynchronous, returns immediately):

GUI: - Click on a Command object (component tree) and click "Execute" button in the top bar. CLI: - command execute <username> <database_name> <command_name>

Attaching a Command to a Timer (gets executed by a Timer): GUI: - Click on a Timer object and attach a Command (top bar button for managing attached Commands) CLI: - timer attach-command <username> <database_name> <timer_name> <username> <database_name> <command_name>


Q. What does "async" mode mean on a Command?

A. By default, a Command runs synchronously — jobs execute one after another in order within a single thread call. In async mode, the Command dispatches its jobs to a thread pool, allowing them to run concurrently.

GUI: - Click on a Command object and enable Async CLI: - command set-async <username> <database> <command_name> <0/1>


Q. Can a Job call another Command?

A. Yes. A Job's task string is a Resolver command, and command execute is a valid Resolver command. So a job can trigger another command:

command add-job alice main MainWorkflow Step1 "command execute alice main SubTaskA"
command add-job alice main MainWorkflow Step2 "command execute alice main SubTaskB"

This enables command chaining and modular automation design — build small, focused commands and compose them into larger workflows.


Q. What types of tasks can a Job perform?

A. Any valid ACE Resolver command string.


Q. How do I grant a user permission to execute a specific Command?

A.

GUI: - Click on a Command object (component tree) and click "Manage Privileges" button in the top bar, select Execute and enter target user's name. Click "Add". CLI: - command add-privilege <username> <db> <command_name> Execute <target_username>

Here Execute is the Command-specific operation selector for the explicit target-user privilege list; it is intentionally different from the registered privilege value name ExecuteCommand used by privilege ... set.


Q. How do I list all jobs in a Command?

A.

GUI: - Click on a Command object (component tree) and click "Manage Jobs" button in the top bar. CLI: - command info <username> <database> <command_name>


Q. Can I reorder jobs within a Command?

A. Yes. Use the change-job-position command:

GUI: - Click on a Command object (component tree) and click "Manage Jobs" button in the top bar, then select a job and move it by clicking on an arrow up/arrow down buttons. CLI: - command change-job-position <username> <database> <command_name> <job_name> <new_position_index>

Job order matters when the command runs synchronously — jobs execute in their stored order. In async mode the order of completion is non-deterministic.

Q. Can a Command be attached to more than one Timer/Component?

A. Yes. A single Command can be attached to multiple Timers or different Components simultaneously. Each Timer/Component fires the command independently on its own schedule. The Command itself is thread-safe, so concurrent timer fires are safe even if two timers happen to fire the same command at the same moment.

Similarly, a single Command can be attached to both a Timer's normal tick and its timeout event.


7. Privilege System


Q. How does the ACE privilege system work?

A. ACE uses a capability-based privilege system with 200+ fine-grained privileges.

Every component type (User, Database, Command, Timer, Variable, Device, Controller, Sensor, SerialPort, PermGroup) has its own privilege definitions. ACE does not combine Environment, User, component, and Permission Group privileges as a simple additive inheritance stack. The exact HasPrivilege path is target-specific. Depending on the target, ACE evaluates administrator/public access, explicit target-specific privileged-user grants, manager status, a relevant base privilege value, and matching live Permission Groups in a defined order.

A Permission Group privilege is not copied into or inherited by the User. A live PermGroup privilege participates only when the requesting User and the target Component are both currently attached to that same PermGroup. Matching PermGroups can override the base privilege result for that check, but they do not revoke an administrator/public/manager result or an explicit target-specific grant that already succeeded earlier in HasPrivilege.

Q. What is the difference between add-privilege on a component vs. privilege set?

A. These are different mechanisms and should not be described as interchangeable or simply additive:

  • <component> add-privilege <PrivName> <target_user> — adds <target_user> to the named privilege container on that specific target component. HasPrivilege checks this explicit target-specific grant before the live PermGroup phase, so a matching negative PermGroup does not revoke that explicit grant.

  • <component> add-privilege <PrivName> #permgroup / <component> remove-privilege <PrivName> #permgroup — performs a one-time snapshot expansion of the PermGroup's current users and adds/removes those users individually in the target component's explicit privilege container. Users added to or removed from the PermGroup later do not retroactively change that earlier snapshot operation.

  • privilege user set <target_user> <PrivName> <0/1> — changes the privilege value stored on that User. Whether that value is a base input to HasPrivilege depends on the target path; for an owned component, for example, it is the owner's base privilege value, not a blanket grant on every arbitrary component in the environment.

  • privilege permgroup set <permgroup_name> <PrivName> <0/1> — changes the privilege value stored on the PermGroup. This is a live group-context rule: it participates when the requesting User and target Component are both currently attached to that PermGroup.

Do not reduce these rules to "direct OR user OR group means access". ACE evaluates them in the target-specific HasPrivilege order.


Q. What are Privilege Presets?

A. Privilege Presets are named templates of privilege configurations. You define a preset once, set which privileges are enabled, and then apply that preset to any user or permission group instantly.

Presets dramatically reduce the time to configure permissions for new users in large environments.


Q. What are Managers?

A. A Manager for a component type is a user who has been delegated the ability to manage all components of that component type within the environment.


8. Permission Groups


Q. What is a Permission Group in ACE?

A. A PermGroup is a named grouping of users and components at the environment level. It serves two purposes:

  1. Live group-context privilege rules — a PermGroup stores privilege values that are evaluated when the requesting User and target Component are both attached to that same group
  2. Group Chat

PermGroups simplify large deployments by grouping the users and components that should share the same live group-context privilege policy. Set a group privilege value with privilege permgroup set <permgroup_name> <privilege_name> <0/1>. The privilege is not copied into each User; current group membership is evaluated during later privilege checks.

For Command execution, ACE intentionally uses two different identifiers in these two privilege mechanisms: component-specific command add-privilege / command remove-privilege uses the operation token Execute, while privilege env|user|permgroup set uses the registered privilege value name ExecuteCommand. For a live Permission Group rule, for example, use privilege permgroup set DevTeam ExecuteCommand 1.

If one or more PermGroups contain both the requesting User and the target Component, ACE evaluates the requested privilege across those matching groups. If any matching group enables it, the group result is positive. If matching groups exist and none enable it, the group result is negative. That group result overrides the base privilege result for the check. It does not override earlier successful administrator/public/manager paths or an explicit target-specific privileged-user grant.

Separately, #permgroup can be used as a bulk target for add-privilege and remove-privilege. That syntax snapshots the group's current users at command time and edits the target component's explicit privileged-user list as if each current member had been specified separately. Later group membership changes do not retroactively alter that snapshot.


Q. How do I create a PermGroup and add users and components to it?

A.

GUI: - "New" button (top-left) -> New Permission Group - Select a PermGroup object from the component tree (left panel) and use toolbar buttons to add/remove users and components CLI:

# Create a group:
permgroup create <group_name>

# Add a user to the group:
permgroup attach-user <group_name> <username>

# Add a component to the group (by UID):
permgroup attach-component <group_name> <component_uid>

Attaching a User to a PermGroup does not copy or inherit all group privileges into that User. For a live PermGroup privilege to participate in an operation, the requesting User and the target Component must both be attached to that PermGroup; ACE then evaluates the group's configured privilege value for that operation.


Q. Can a PermGroup contain components from different databases?

A. Yes. PermGroups operate at the environment level. Components from any database, owned by any user in the environment, can be added to the group. The UID-based lookup is environment-wide.


Q. What is the PermGroup chat feature?

A. Each PermGroup has a group chat conversation.

ACE's built-in chat system supports both DM (user-to-user) and group (PermGroup) conversations.


Q. How do PermGroup privileges interact with user-level privileges?

A. They are not a simple additive inheritance model. A User does not receive a copied set of all privileges from every PermGroup they belong to.

For a live PermGroup rule to participate, the requesting User and the target Component must both be attached to the same PermGroup. ACE checks the privilege value configured on each such matching group for the requested operation. If at least one matching group enables the privilege, the group result is positive. If matching groups exist and none enable it, the group result is negative. That result overrides the base privilege result for that check.

The group phase is later than explicit target-specific grants and the administrator/public/manager paths. Those successful paths return earlier, so a negative PermGroup does not revoke them. Likewise, <component> add-privilege ... <user> is an explicit target-specific grant and is not the same mechanism as a live PermGroup value.

#permgroup used as an add-privilege/remove-privilege target is different again: it is only a one-time snapshot of the group's current users. Because live PermGroup checks use current membership, adding a User later can make an already-configured live group privilege participate in future checks when the target Component is also in that group; it does not require re-running privilege permgroup set. An earlier add-privilege ... #permgroup snapshot, however, is not dynamically updated by later membership changes.


9. Timers


Q. What is a Timer in ACE?

A. A Timer is a recurring or one-shot event emitter that fires attached Commands on a schedule. Timers are the primary scheduling mechanism in ACE.

Key properties: - Interval — how often the timer fires (in ms, or human-readable string like "5s", "1m30s", "2h") - Repeats — how many times to fire (-1 = infinite loop; 0 = one-shot) - Launch mode — LAUNCH_EXECUTE (fires immediately, then waits one interval) or LAUNCH_WAIT (waits one full interval before first fire) - State — Stop, Start, Restart - Remote type — SERVER, CLIENT, or HYBRID - LaunchOnLoad — automatically start when the environment loads - RecoverTime — recover the remaining interval after a server restart/crash


Q. What is the difference between SERVER, CLIENT, and HYBRID timer modes?

A.

Mode Behavior
SERVER Timer always ticks on the server. No client needed. Default mode.
CLIENT Timer ticks on the remote client. No client connected → timer does not start. Client disconnects while running → timer stops (no server fallback).
HYBRID Automatically runs on the client when at least one connection is present; falls back to the server when no client is connected. Handoffs are phase-preserving: the new runner picks up exactly where the previous left off, waiting I − elapsed_mod ms before its first fire, so no fires are skipped.

HYBRID is the most robust mode for mixed server/client deployments where continuity matters.

Multiple simultaneous connections: The HYBRID timer runs on exactly one client connection at a time — never on all of them simultaneously. On the user's first login the timer is handed off from the server to that connection. When that connection drops, the timer is automatically reassigned (phase-preserving) to the first remaining connection. Only when all connections drop does the server take over as fallback. This prevents duplicate fires when the same user is logged in from several clients at once.


Q. How do I attach a Command to a Timer?

A.

GUI: - Select a Timer object from the component tree (left panel) and use toolbar button to manage attached commands. CLI:

# Attach to fire every tick:
timer attach-command <username> <db> <timer_name> <cmd_username> <cmd_db> <cmd_name>

# Attach to fire on timeout (when repeats finish, or timer is stopped):
timer attach-command <username> <db> <timer_name> <cmd_username> <cmd_db> <cmd_name> --on-timeout


Q. Can I set a start time for a Timer?

A. Yes. Use timer set-start-time or timer set-start-timestamp to schedule a timer to begin at a specific time in the future. To unset / clear the timer start time, run either command without the final date/time or timestamp value; ACE stores the cleared start time as timestamp 0:

GUI: - Select a Timer object from the component tree (left panel) and configure the date & time. - Use the Timer panel's Unset start-time button to clear the start time. CLI:

timer set-start-time <username> <db> <timer_name> [<DD.MM.YYYY-hh:mm:ss>]
timer set-start-timestamp <username> <db> <timer_name> [<unix_ms_timestamp>]

# Unset / clear the start time:
timer set-start-time <username> <db> <timer_name>
timer set-start-timestamp <username> <db> <timer_name>


Q. How do I clear a Timer message?

A. Run timer set-message without the optional message argument:

timer set-message <user_name> <database_name> <timer_name>

In the GUI, clear the Timer Message field and press Set. An empty field is sent intentionally and clears the stored message. Supplying text still sets the message normally.


Q. What happens if I change Repeats or Launch Type while a Timer is already running?

A. The current run continues with the settings it started with. The new Repeats or Launch Type value is stored for the next run, and ACE logs a warning that the change takes effect after the Timer is restarted. Changing either setting does not restart the Timer automatically.


Q. What does LaunchOnLoad do?

A. When LaunchOnLoad = true, the timer automatically starts (timer start) as part of the environment load sequence. This means automation workflows based on timers survive server restarts without manual re-activation.

GUI: - Select a Timer object from the component tree (left panel) and configure the LaunchOnLoad CLI:

timer set-launch-on-load <username> <db> <timer_name> <0/1>


Q. What does RecoverTime do?

A. When RecoverTime = true, the timer attempts to recover its phase after a server restart. Instead of treating the restart as if no time had passed, it uses the stored start timestamp (timer set-start-timestamp) to calculate how much of the current interval has already elapsed, then starts with the remaining milliseconds of that interval — so the fire schedule continues as if the server never went down.

GUI: - Select a Timer object from the component tree (left panel) and configure the RecoverTime CLI:

timer set-recover-time <username> <db> <timer_name> <0/1>


Q. What interval formats can I use for Timer intervals?

A.

  • "500" or "500ms" — 500 milliseconds
  • "5s" — 5 seconds
  • "1m" or "1m30s" — 1 minute, or 1 minute 30 seconds
  • "2h" — 2 hours
  • "1D" — 1 day
  • Combinations: "1h30m45s", "2D3h", etc.

10. Variables & Expressions


Q. What is a Variable in ACE?

A. A Variable is a named, string-valued container that lives inside a Database. Variables hold arbitrary data — sensor readings, counters, timestamps, configuration values, state flags — and serve as the reactive data bus of ACE automation.

Variables integrate with the Expression engine: you can define conditions on a variable's value that automatically fire Commands when the condition is met (or transitions occur).


Q. How do I use a Variable?

A.

CLI: Use its value inside any expression or job task:

VAR{username;database;variable_name}

The VAR{...} syntax expands the variable's current value inline inside any Resolver command that processes expressions. For example:

device set-value alice main Relay heater VAR{alice;main;heaterState}


Q. Can ACE generate a cryptographically secure random string in an Expression?

A. Yes. Use RANDSTR{length} or RANDSTR{length;mode}. The mode is optional and defaults to ALNUM (alphanumeric).

RANDSTR{32}          # 32 characters, ALNUM by default
RANDSTR{32;ALNUM}    # A-Z, a-z, 0-9
RANDSTR{8;NUMERIC}   # 0-9
RANDSTR{24;ALPHA}    # A-Z, a-z
RANDSTR{64;HEX}      # 0-9, a-f

The requested length must be from 1 to 1024 characters. Mode names are case-insensitive.

RANDSTR is designed for values that must be unpredictable. It uses OpenSSL's private cryptographically secure random number generator (RAND_priv_bytes) and rejection sampling for unbiased character selection. If the cryptographic RNG reports a failure, ACE fails the expression instead of falling back to RANDOM{}, a timestamp, or another weaker source.

Cryptographic randomness does not by itself guarantee that two independently generated strings can never collide. If a value must also be unique in a persistent namespace, choose an appropriate length and enforce uniqueness where the value is stored.


Q. What is a VarExpression?

A. A VarExpression is a conditional rule attached to a Variable. It defines:

  • CompareType — INT, DECIMAL, or STRING
  • Operator — ==, !=, <, <=, >=, >
  • Value — the reference value to compare against
  • TriggerMode — when to fire attached commands
TriggerMode Fires when
LEVEL The expression matches (fires on every SetValue where condition is true)
EDGE_RISING Condition transitions from not-matched → matched
EDGE_FALLING Condition transitions from matched → not-matched
EDGE_ANY Any transition (matched↔not-matched)
VALUE_CHANGED The variable's value changes to anything new
SUSTAINED Condition has been continuously matched for a configured duration. Fires once by default, or once per duration for as long as the condition holds — see --continuous below

Q. How can I check what a Variable will actually do, without reading every expression setting?

A. Use variable show-behavior — it prints one plain sentence per expression, in declaration order, describing exactly when that expression fires and which commands it runs:

variable show-behavior <user_name> <database_name> <variable_name>

Example output:

[exp1] When the value becomes >= 30, 'cool_down' runs once.
[exp2] While the value is < 10, 'heat_up' runs on every update of the value.
[exp3] Every time the value changes, 'log_it' runs (the condition != 0 is not checked).
[exp4] When the value has been > 90 continuously for 2m, 'alarm' runs once.

An expression with no attached command is reported as running "no command (nothing happens)", so an unfinished configuration is obvious at a glance. A variable with no expressions at all reports that it never runs a command.

In the GUI, the Show Behavior button on the Variable toolbar runs the same command and prints the result to the log panel.

The command is gated on the ListExpressions privilege — the same privilege as variable info ... AddedExpressions, because it discloses exactly the same data, only rephrased.


Q. What is the SUSTAINED trigger mode?

A. SUSTAINED fires a command only when the expression condition has been continuously true for a duration you choose. A brief spike that clears before the duration is up fires nothing — which is exactly what it is for: reacting to a condition that has lasted, not to a flicker.

By default it fires once. Once it has fired, it does not fire again until the condition has become false and true again — so a condition that stays true for an hour still produces a single run.

If you want it to keep firing while the condition holds, add --continuous (see the next question).


Q. Can a SUSTAINED expression keep firing while the condition stays true?

A. Yes — add --continuous to the expression. It turns a single reaction into a repeating one: the command runs once per duration for as long as the condition remains true, and stops as soon as the condition clears.

variable add-expression <user> <database> <variable> <expression_name> <compare_type> <operator> <value> SUSTAINED:<duration> --continuous

Example — warn every 30 seconds while a tank stays above 90, instead of once:

variable add-expression alice main level overfill INT > 90 SUSTAINED:30s --continuous

How the two behave

Setup Condition true for 10s Condition true for 95s Condition clears and becomes true again
SUSTAINED:30s never fires fires once (at 30s) fires once more
SUSTAINED:30s --continuous never fires fires at 30s, 60s, 90s — 3 times starts counting again from zero

Points worth knowing:

  • The first run is never early. In both setups nothing happens until the condition has held for the full duration.
  • Clearing the condition resets the clock. A condition that keeps flickering below the duration never fires, in either setup.
  • It only applies to SUSTAINED. Adding it to any other trigger mode is rejected with an explanation, rather than accepted and quietly ignored — a typo can never leave you waiting for a repeat that was never configured.
  • Order does not matter. --continuous may be written anywhere after the trigger; the value being compared is read the same way with or without it.
  • It is saved with the expression and survives restarts. Expressions created before this option existed keep loading as one-shot.

Where to set it:

  • CLI — the --continuous flag shown above.
  • GUI — the Repeat while the condition stays true checkbox in the Manage Expressions dialog, next to the sustained duration. It is only enabled for the SUSTAINED trigger mode.
  • Node Editor / Command Builder — the same option is offered when you build the expression there.

To check what an existing expression will do, use variable show-behavior — a repeating expression is described as running once every duration for as long as the condition stays true, a one-shot one as running once.


Q. Can a Variable read its value from a file?

A. Yes. From ACE File object and stores it as the variable's value:

GUI: - Select a File object from the component tree (left panel) and use toolbar buttons. CLI:

file set-variable <path> <username>[@<environment>] <database_name> <variable_name>
file read <path>

Additionally, when the file is modified and monitoring is active (file start-monitoring), the variable is automatically updated to the file's new content — creating a file-to-variable reactive binding.

Files (and Webhooks) are shared between environments. When the variable you want to link lives in a different environment than the session you are logged into, qualify the user with that environment: file set-variable /data/log.txt john@production main temperature. A bare <username> keeps the current environment. If a username itself contains @, the exact local username takes precedence — the token is only interpreted as user@environment when no user of that full name exists in the current environment. Cross-environment linking also requires a grant from the target side: the variable must be public, or a user with your username in the target environment must be on the variable's VariableSetValue privilege list (the target environment manages grants via variable add-privilege) — being an admin of your own environment is not enough to write into another environment.

In the GUI's Set Variable dialogs, the Environment field is pre-filled with the environment you logged into. On the Server GUI the dropdown also lists every other activated environment on the server; on the Client GUI the list of foreign environments is not exposed, so the field stays editable — type the environment name for a cross-environment target.


11. Devices, Controllers & Sensors


Q. What is a Device in ACE?

A. A Device represents a controllable pin/device. device set-value ... TOGGLE switches the current authoritative server-side value between 0 and MaxValue.


Q. After a board reset, ACE shows the old value but the hardware is off. How do I align them?

A. Two complementary mechanisms:

  • Push (default): with set-resend-value-start enabled (the default), the server re-sends each device's stored value to the freshly-booted board on start, so the hardware is restored to what ACE shows.
  • Pull: use device read-value (or the Device panel's Read Value button) to read the device's real pin value back from the controller and update the stored/displayed value to match.
device read-value <user_name> <database_name> <'Device'/'Relay'/'Light'/'Led'> <device_name> <'digital'/'analog'> ['--dont-print-value']

This works for both local and remote controllers — the read request is routed to the controller's host exactly like a set-value write. Because a device is an output, a digital read reports the level the board is currently driving on the pin (its output register), which confirms what the microcontroller is asserting and reveals any server↔board desync. It does not sense the downstream load (a relay coil or LED circuit), and a PWM/analog output duty cycle cannot be read back. The GUI button refreshes the panel automatically so a changed value shows immediately.


Q. What is a Controller in ACE?

A. A Controller represents a physical microcontroller board.

Controllers can have Devices, SerialPorts, and Sensors attached to them. A controller can be associated with a RemoteUser (the client user that physically manages the hardware connection).


Q. Where can my controller hardware be connected — and how do commands reach it?

A. Anywhere in your ACE installation. You always use the same commands (or GUI buttons) no matter where a board is plugged in — ACE finds the right path to it automatically:

Where the board lives What happens
On the Server machine (e.g. an Arduino on the server's USB) The server talks to the board directly.
The Server machine is itself a Raspberry Pi, host mode on The server drives the Pi's own GPIO pins directly — no network involved at all.
The Server machine is itself a Raspberry Pi, host mode off The Pi's GPIO pins stay unused, but boards attached to the machine's serial ports work normally.
On a Client machine The command travels from the server to that client. ACE picks the client that actually has the board's port open, so with several clients online the request always reaches the machine the board is plugged into. If that client is offline, a "user is not online" warning is logged instead of the command being silently lost.
The Client machine is a Raspberry Pi in host mode (paired) GPIO reads and writes travel to that exact Pi. The Pi is recognized by its hardware serial number, not its network address — so the pairing keeps working after the Pi gets a new IP address, reboots, or comes back from a power outage.
The Client machine is a Raspberry Pi, not in host mode It behaves like any normal client: Arduinos and serial devices plugged into it work as usual; only the Pi's own GPIO pins stay unused until you pair it.
Raspberry Pi Embedded The Pi runs the dedicated ACE Embedded firmware and behaves like a smart controller board: you connect it by its serial line (like an Arduino) to whichever machine hosts the port, and it can also drive its own GPIO pins and sensors by itself.

A Raspberry Pi client does not have to be paired: pairing is only needed when you want to use the Pi's own GPIO pins (host mode). A Pi acting as a normal client — including one with Arduinos or other serial devices attached — needs no pairing at all.


Q. How do I prepare (flash) a brand-new Arduino before using it?

A. A freshly bought Arduino still carries the factory sketch and reports no port UID, so ACE can't link it to a SerialPort yet. You first flash the ACE firmware onto it. Two equivalent ways:

  • GUI: open the Prepare Arduino dialog (toolbar button), pick the detected port, choose the model (Uno / Mega2560) and click Upload Firmware. The port list auto-refreshes every second.
  • CLI / Resolver: run
controller prepare <port_name> <model>

for example controller prepare ttyACM0 Uno (or COM3 Mega2560 on Windows). This flashes the bundled ACE firmware and runs as the current user (requires the CreateController privilege) — no controller or database needs to exist yet.

Flashing a custom .hex is an advanced, administrator-only action: pass it as an extra argument (controller prepare ttyACM0 Uno '/path/to/custom.hex'). For non-administrators the custom path is rejected, and in the GUI the "Advanced settings" custom-firmware picker is hidden entirely.

After the board is prepared, link it with controller create and controller attach-serialport.


Q. How do I pair a Raspberry Pi in host mode (direct GPIO over the network)?

A. Host mode lets a Raspberry Pi that runs an ACE Client drive its GPIO pins directly, with the Server routing device/sensor reads and writes to it over the network — no serial board needed. Pairing is a two-step handshake:

  1. Generate a Pair ID for the RaspberryPi controller — on the Server, or from any logged-in Client (the full 6-argument form is forwarded to the Server):
controller pair <user_name> <database_name> RaspberryPi <controller_name>

The server prints Generated Pair ID: <id> and enables host mode on the controller.

  1. On the Raspberry Pi, log in with an ACE Client (CLI, or the GUI's bottom-panel terminal) as the user who manages the board and complete the pairing with the ID from step 1:
controller pair <pair_id>

The client sends the ID together with the board's hardware serial number (from /proc/cpuinfo). The server stores that hardware ID as the permanent Pair ID, marks the controller Remote, binds it to your user, and both sides report success (or a failure for a wrong/already-used ID).

From then on, device set-value / device read-value / sensor reads for components attached to that controller are routed to the paired Pi (the pairing survives server restarts). To undo it, run controller unpair <user_name> <database_name> RaspberryPi <controller_name> on the server.

env export-script deliberately does not export this remote pairing handshake or the Pi hardware ID. After loading an exported environment, a remote Raspberry Pi Client must be paired again from that physical Pi.

Pin numbering: host-mode GPIO uses BCM (Broadcom) GPIO numbers, not physical header pin positions. pin 16 means GPIO16, which sits on physical header pin 36 — physical header pin 16 is a different pad (GPIO23). If a device "writes fine but always reads 0" (or never reacts at all), the first thing to check is that the wire is on the BCM GPIO you attached the device to, not on the same-numbered physical pin.

From the GUI (Controller panel → PairID field + Pair/Unpair buttons):

  • On the Server GUI (and non-Pi clients) the PairID field is read-only and Pair = step 1: it generates the Pair ID and shows it in the field.
  • On a Raspberry Pi client GUI the field is editable and Pair = step 2: it sends the field's Pair ID together with this machine's hardware ID. After the controller info refreshes, the field already contains the server-generated Pair ID, so completing the pairing is normally a single click; type/paste the ID only if the panel has not refreshed since generation. The bottom-panel terminal (controller pair <pair_id>) does the same thing.
  • The field reads NOT_SET before any pairing, shows the generated Pair ID after step 1, and shows the Pi's hardware ID once the pairing completed.

Q. How do I enable host mode when the Raspberry Pi IS the Server (pairing "to itself")?

A. When the ACE Server binary itself runs on the Raspberry Pi (aarch64 Linux, launched as root), there is no client and therefore no handshake at all — the server drives the Pi's own GPIO pins directly. The two-step flow above only exists so a remote client can prove which physical machine it is; the server doesn't need to prove anything to itself.

Setup on the Pi server:

  1. Create the controller locally, as usual (do not mark it Remote / set a RemoteUser):
controller create <user_name> <database_name> RaspberryPi <controller_name> <number_of_pins>
  1. Enable host mode with the same pair command:
controller pair <user_name> <database_name> RaspberryPi <controller_name>

This is step 1 of the network flow — but for a local controller it is also the last step. The printed Generated Pair ID is only consumed by remote clients; for a local controller it is inert and there is nothing to type it into. Ignore it — host mode is already on (visible in controller info as Host).

  1. Attach devices/sensors to pins (controller attach-device / attach-sensor) and use them normally — device set-value, device read-value and sensor reads go straight to the Pi's GPIO with no network involved.

Notes:

  • Pin numbers are BCM GPIO numbers (see the pin-numbering note above), restricted to the pure GPIO pins 5 6 16 17 22 23 24 25 26 27.
  • Direct GPIO requires the server to run as root (the launcher/installer set this up).
  • controller unpair turns host mode back off; the machine keeps working as a normal server (serial boards on its USB ports are unaffected either way).
  • On a Linux/aarch64 Raspberry Pi Server, env export-script preserves this local Host state by emitting the full controller pair <user> <db> RaspberryPi <controller> command for a Host, non-Remote RaspberryPi controller. It never serializes the generated Pair ID.

Q. What is a Sensor in ACE?

A. A Sensor reads data from physical inputs/controller pins.

Sensors can have Variables bound to them, so that every new reading automatically updates a variable's value — which can then trigger VarExpressions & Commands.

The four CLI sensor types are Sensor, RFIDReader, EventSensor, and Button. General sensor commands accept all four. RFID tag commands (auth-tag, deauth-tag, set-tag-label, set-tag-command, unset-tag-command, view-tag-info, and set-response-type) accept only RFIDReader. Event-trigger commands (add-event-sensor-command / remove-event-sensor-command) accept EventSensor and Button. start-listening / stop-listening accept RFIDReader, EventSensor, and Button.


Q. Which pins can an EventSensor (button, PIR, motion/contact switch) use on an Arduino?

A. An EventSensor is not polled — the board watches it with a pin-change interrupt and reports only when the level actually changes. So the pin has to be one the board can raise that interrupt on, and on the Mega 2560 most pins cannot.

Board Pins an EventSensor can use
Arduino Uno every digital pin, except 1 (TX) and 13 (on-board LED)
Arduino Mega 2560 10, 11, 12, 50, 51, 52, 53 and A8–A15 (62–69) — nothing else (13 is the on-board LED and is excluded)

Both controller create presets are one pin short of the board they name: Uno makes 19 pins (0–18) and Mega2560 makes 69 (0–68), so the board's last pin — Uno D19, Mega A15 (69) — is not there to attach to. Create the controller with an explicit pin count of 20 / 70 if you need it. The warning below never suggests a pin the controller does not carry.

Two traps on the Mega specifically:

  • D2, D3, D18–D21 do not work, even though an Arduino pinout labels them "interrupt" pins. Those are external interrupts (INT0–INT5); ACE uses pin-change interrupts, which on the ATmega2560 exist only on PORTB and PORTK.
  • A pin outside the list fails silently on the board. The registration frame is accepted off the wire (the board's RX LED blinks, and start-listening / stop-listening appear to work), but the firmware installs no interrupt, so no change is ever reported — while ACE still shows the sensor as attached and Listening. The Server warns when a sensor is attached to, or armed on, such a pin, and the warning names the pins that do work.

A Raspberry Pi (host mode or Embedded) has no such restriction — any pure GPIO pin works, and the attach already refuses one that is not.


Q. A sensor on a Raspberry Pi's own GPIO reports nothing — where do I look?

A. A Raspberry Pi in host mode — whether it runs as the Server or as a Client — watches its own pins and its own SPI reader in-process, not over a serial link. That path used to fail silently in several different ways, all of which looked identical from outside: a sensor that reports itself attached, a start-listening that appears to succeed, and nothing ever happening.

It now says which one it is, in the log:

What you see What it means Fix
The SPI bus could not be claimed… The reader is not listening. This is the one to expect if the LED works but the reader does not: /dev/spidev0.N is a different permission from the GPIO character device, so a process that drives a pin perfectly can still be unable to open the bus. Enable SPI (dtparam=spi=on, then reboot), and run as root or add the user to the spi group.
The GPIO pin could not be armed… The line is in use by another process, or this process may not claim it. Free the line, or run with rights to claim it.
This sensor was marked as listening but nothing was watching it… A stale listening flag was found with no watcher behind it, and the watch was started. Before, start-listening answered "already listening" and left it dead permanently. Nothing — it repaired itself.
A local sensor watch could not handle a reading and dropped it… Something attached to the reading threw. The line names the sensor, the reason, and +<n> for identical failures folded into it (at most one line a minute per reason). Fix what the reason names — usually a job in the attached command.
A tag was scanned but it is not on this reader's authorised list… The scanned ID does not match any authorised tag. sensor auth-tag with the ID exactly as the line prints it.
A tag was scanned and it is authorised, but nothing is attached to it… The tag is authorised and carries no command. sensor set-tag-command.
Nothing at all from a Button, while an EventSensor/PIR on the same Pi works Fixed. The watcher compared the raw pin level against the button's logical level, which is its inverse, so every press was discarded. Update — no configuration change needed.

Why Embedded mode was never affected. An Embedded Pi rebuilds its sensor objects from the board's registration frames on every connect, so a stale listening flag cannot survive; and it is normally started by its systemd service as root, so it can always open the SPI bus. A Server or Client you launch yourself has neither of those things going for it.

The last three rows are raised wherever the command actually runs: on the host Pi itself for a Server, and on the server for a reader on a client Pi or on an Arduino.


Q. Is a scanned RFID tag the same string on an Arduino and on a Raspberry Pi?

A. Yes, and it has to be: ACE compares scanned tags as plain strings, so the formatter is a tag's identity. sensor auth-tag stores the text you give it, and a reader that formatted the same card differently would silently refuse a tag you had already authorised.

Both readers produce four bytes, uppercase, zero-padded, colon-separated — 49:49:C4:8B — whatever the card's real UID length. Four is what the Arduino firmware has always emitted, so every already-authorised tag keeps working.

The trade-off, stated plainly: two cards whose UIDs differ only after the fourth byte are one tag to ACE. Most access cards (MIFARE Classic) have 4-byte UIDs and are unaffected; 7- and 10-byte UIDs (NTAG, Ultralight) are truncated. This was already true of every Arduino-read tag — the Pi now agrees with it instead of disagreeing.


Q. How does RFID access control work in ACE?

A. The RFID workflow:

  1. Create an RFID sensor
  2. Attach it to the controller
  3. Start listening
  4. Authorize specific tag/card IDs
  5. Optionally label tags.
  6. Attach a command to fire on authorized tag read or on other events (can be tag-specific)

When an authorized tag is scanned, ACE fires the tag-specific command. Unauthorized tags are rejected and can trigger an optional reject command.

Each tag carries at most one command. Setting a command for a tag replaces whatever was set before; sensor unset-tag-command clears it. A command may also be set for a tag that is not authorized — it is then kept for unauthorized scans of that tag.

In the GUI this is split across two Sensor toolbar buttons, so neither dialog mixes two unrelated jobs:

  • Added Tags — authorize (add), deauthorize (remove) and relabel tags. The list shows <tag id> — <label>, and selecting a tag fills the ID and Label fields with its current values.
  • Tag Commands — pick a tag, see the command currently set for it (<user> / <database> / <command>), then set a different one or unset it. Tags that are not authorized but do have a command are listed here too, so such a command can still be reviewed and removed.

Both lists are single-selection, and the per-tag buttons stay disabled until a tag is selected.

CLI equivalents:

sensor auth-tag <user> <db> RFIDReader <sensor> <tag_id> [<tag_label>]
sensor deauth-tag <user> <db> RFIDReader <sensor> <tag_id>
sensor set-tag-label <user> <db> RFIDReader <sensor> <tag_id> <tag_label>
sensor set-tag-command <user> <db> RFIDReader <sensor> <cmd_user> <cmd_db> <cmd_name> <tag_id>
sensor unset-tag-command <user> <db> RFIDReader <sensor> <tag_id>
sensor info <user> <db> RFIDReader <sensor> AuthedTags
sensor info <user> <db> RFIDReader <sensor> TagCommands

Q. What is the calc-value-formula on a Sensor?

A. The calc-value-formula allows you to transform the raw sensor reading before storing it as the sensor's value. This is a formula string using ACE's expression syntax. The special token sensor_value is substituted with the raw reading at evaluation time.

For example, if a temperature sensor outputs a raw 0–1023 ADC value, you can map it to a 0–100 scale:

sensor set-calc-value-formula alice main Sensor thermometer MAP{sensor_value,0,1023,0,100}

The sensor_value placeholder is replaced with the raw reading. The result of the formula becomes the sensor's reported and used value. This saves you from writing a separate Variable + Expression chain just for unit conversion.


Q. What is a SerialPort in ACE?

A. A SerialPort abstracts a hardware serial communication port (COM port on Windows, /dev/tty* on Linux).

SerialPorts are the physical bus between ACE and hardware (controllers, RFID readers, relays, etc.).


12. Files, Webhooks & Processes


Q. What is an ACE File object?

A. A File object is ACE's abstraction over filesystem files.


Q. How do I record what my automation is actually doing?

A. Use ACE's built-in file logging — file create-log once, then file write-log wherever it matters. This is strongly recommended for any environment running real logic, and it is the single most useful thing you can do before you need it.

ACE does not write any log file of its own — the only file logs that exist are the ones you write. So unless you write them, nothing on disk records which expression fired, which branch a command took, or what a sensor actually read at the moment a door failed to open. When automation misbehaves at 3 a.m., the difference between a five-minute fix and a day of guessing is usually whether that log exists.

file create-log logs/door.log
file write-log INFO logs/door.log Door-opened-by-CARD01

write-log takes the level first — INFO, WARNING or ERROR — then the path, then the content. logs/ is the portable alias for the active environment's logs directory, and it is the form file list-logs reports back, so the same line works on any machine. An absolute path also works.

Every entry is stamped automatically in UTC, so entries from machines in different time zones stay directly comparable:

[27.07.2026~13:19:51 UTC] [INFO]: Door-opened-by-CARD01

Add --no-time before the content to omit the timestamp.

What makes it worth adopting as a habit:

  • You choose what is worth recording. ACE logs its own operations; only you know which of your values, branches and thresholds matter.
  • It works from everywhere logic runs — commands, timers, variable expressions, event sensors, scripts, and the node editor's File Write Log node (Utility section).
  • The timestamp is unambiguous. UTC is written into the stamp itself, so logs from servers in different zones stay comparable.
  • It costs nothing to leave in. file clear-log truncates a log, file set-remove-on-exit discards it on shutdown, and file view-log <path> <lines> reads the tail back without leaving ACE.

A practical habit: log the inputs to a decision and the branch taken, not just the outcome. Sensor-read-847-threshold-500-opening explains itself six months later; Opened does not.


Q. How do I read a log while ACE is running?

A. Through ACE — you cannot read it from the filesystem, and that is deliberate. While ACE holds a log open, Windows keeps the handle exclusive (no other process can open the file at all) and Linux takes an advisory write lock and leaves the file mode 0100. Nothing outside ACE gets in.

Two ways in, both administrator-only:

  • GUI — the View Logs button on the Environment toolbar. Pick a log from the drop-down (it lists every log that exists), read the newest page, and walk backwards through the file with < Older / Newer >. Page size is adjustable. It works identically from a Client: the window sends the same commands the terminal would, and the server resolves them against your session.
  • CLI or the GUI terminal — file view-log.
file view-log <file_path> <'number_of_lines'/'all'> [<page>] [<anchor>]
file log-size <file_path>
file list-logs                         // what exists
file view-log logs/door.log 200        // the newest 200 lines
file view-log logs/door.log 200 1      // the 200 before those
file view-log logs/door.log all        // everything (page is ignored)

page is optional and defaults to 0, so every existing command keeps meaning what it did. Page 0 is the newest block; each higher page steps one block further back. A page shorter than you asked for is the start of the file.

anchor is optional too, and only matters when you page through a log that is still being written to. Boundaries are measured back from the end of the file, so a log that gains lines between two requests slides the next page by that many — page 1 repeats part of page 0 and skips as many older lines. file log-size returns a token for the file as it is now; pass it back with every page and the server counts what has been appended since and pushes the window back by exactly that much, so the pages stay contiguous:

file log-size logs/door.log            // e.g. 48213.0
file view-log logs/door.log 200 0 48213.0
file view-log logs/door.log 200 1 48213.0   // contiguous with the page above

The token also carries the log's generation, so an anchor taken before a file clear-log is ignored rather than trusted — a cleared log that regrows passes through its old sizes again, and the byte count alone could not tell. The View Logs window does all of this for you.

Both commands require an administrator account, which is the intended policy: an administrator reads the logs their users write. A non-administrator does not see the button, and the command answers "access denied".

They are also scoped to your own environment. A log belongs to the environment that created it - that is literally where it sits on disk, in that environment's logs/ directory - and one ACE server can host environments for several customers. file list-logs shows only yours, and a log belonging to another environment is reported as not registered rather than refused, because a refusal would confirm it exists. The same applies to file view-log, file log-size, file clear-log, file remove-log, file write-log and file set-remove-on-exit. The owner is recorded when the log is created and stored with it, so it survives a restart even for a log kept at an absolute path outside the environment's logs directory.


Q. What is logs/ in a log path?

A. logs/ is the active environment's own logs directory. It is an alias ACE expands for you, not a folder relative to wherever the process happens to be running:

file create-log logs/door.log
file write-log INFO logs/door.log Door-opened-by-CARD01
file write-log INFO logs/site-a/door.log Door-opened-by-CARD01

Both a plain file name and a subdirectory below it work; missing subdirectories are created. Use it in preference to an absolute path, for three reasons:

  • It is portable. The same command, script, job or node graph works on another machine, and on a Server and a Client, without editing the path — each one resolves logs/ to its own environment's logs directory.
  • It survives paths with spaces. ACE's command language splits arguments on whitespace and has no quoting, so a real path such as C:/Users/John Smith/ACE/… cannot be passed as one argument at all. logs/door.log can.
  • It is what ACE reports back. file list-logs prints a log that lives in the environment's logs directory in exactly this form, so the listed line can be pasted straight into file write-log, file view-log or file clear-log, and it is what the node editor's File Write Log node offers in its suggestions.

An absolute path is still accepted and used exactly as given. What is not accepted is escaping out of the logs directory with .. — logs/../../secrets.txt is refused, so a log path coming from a script or a graph cannot be turned into a write anywhere on disk.


Q. What are script files (.asf)?

A. ACE Script Files (.asf, ACE Script File) are text files containing structured ACE Resolver commands organized into sections. A script file can be executed as a unit — each section runs as a group of commands.

Section types: - #run — the main entry point section - #section <name> — synchronous section - #async_section <name> — runs on a separate thread - #full_async_section <name> — each instruction inside of the full_async_section runs on a separate thread

The #ACE header is required at the top. Scripts are validated against a Virtual Environment before execution to catch errors without side effects.


Q. What is an Inbound Webhook?

A. An Inbound Webhook allows external services to trigger ACE automation by sending an HTTP POST request to the ACE server.

The endpoint is POST https://<host>:4541/webhook/<token> — it is served by the HTTPS listener on port 4541 (the same one the web dashboard uses), not by the ACE protocol port 4542. webhook create, webhook info and the integration setup all print the full URL with the correct port; if a request is aimed at 4542 anyway, the server now logs a warning naming the right port instead of letting the connection time out.

curl -k -X POST https://<host>:4541/webhook/<token> \
     -H "Content-Type: application/json" \
     -d '{"key": "value"}'

If a shared secret is configured, the request must carry a matching X-Hub-Signature-256 (HMAC-SHA256, GitHub style) or X-Gitlab-Token header, otherwise it is answered with 401. A disabled webhook answers 404.


Q. What is an Outbound Webhook?

A. An Outbound Webhook lets ACE call external HTTP endpoints from automation logic.

ACE also supports OAuth2 client credentials flow for outbound webhooks (including device authorization / RFC 8628 device code flow), with automatic token caching and refresh.

Outbound webhook bodies support two kinds of dynamic content:

  • {{KEY}} placeholders: replaced from KEY=VALUE arguments supplied to webhook post, webhook call, or webhook call-sync.
  • ACE expressions: VAR{...}, CMD{...}, CALC{...}, IF{...} and nested expressions are evaluated immediately before the HTTP request is sent.

Examples:

webhook template set alert '{"content":"{{CONTENT}} Temperature: VAR{admin;plant;temperature}"}'
webhook call alert 'CONTENT=Temperature update.'
webhook template set alert '{"state":"IF{COMPARE{VAR{admin;plant;temperature};>;30};hot;ok}"}'

Important parsing rule: double-brace blocks are always placeholder keys, not expressions. {{CONTENT}}, {{CMD}}, and {{CMD{VAR{x}}}} are treated as keys/placeholders and are not executed. A real expression uses single braces after an ACE expression prefix, for example CMD{admin;db;command} or CALC{1+VAR{x}}.


Q. What are Webhook Integrations?

A. Webhook Integrations are pre-built connectors for popular services (e.g., Slack, Discord, custom APIs). They provide a guided setup flow.

Integrations build on top of the standard outbound webhook infrastructure but pre-configure headers, URL templates, and body formats for specific services.


Q. How do I create a plain webhook, without any integration?

A. An integration is only a convenience — a webhook is a first-class object on its own, and you can create a bare one with defaults and customise it afterwards.

CLI:

webhook create <name> outbound
webhook create <name> inbound

GUI: open the webhook dialog (New → New Webhook, or the Integration Setup toolbar button) and leave the service selector on its first entry, "Plain webhook (no integration)" — it is selected by default. Pick a name and a direction (Outbound = ACE calls a URL, Inbound = ACE receives a call) and press the setup button.

Everything else stays at its default and is edited afterwards from the Webhook toolbar: target URL, HTTP method, body template, headers, secret, OAuth2, response-to-variable mapping and links.


13. Node Editor & Scripting


Q. What is the ACE Node Editor?

A. The Node Editor is a visual, drag-and-drop design tool built into the GUI. It allows you to build ACE Resolver commands, job task strings, variable expressions, complete script files (.asf) and easily sharable graph files (.acegraph) by connecting graphical nodes.


Q. How is the Add Node palette organised?

A. The palette — reachable from the Add Node button in the top left or by right-clicking the canvas — is grouped into sections:

  • Recent — always the first section once you have added anything. It lists the node types you used most recently in this session, so repeating the same handful of nodes does not mean scrolling the whole palette each time. Entries are filtered by context exactly like the rest of the palette, so a Script node used in the Script Builder is not offered in an editor that cannot compile it.
  • Then, in the normal Node Editor: Utility, Command, Query, Expression, Output, Comment.
  • In the Script Builder: Script (the primary content there), then Utility, Command, Query, Expression, Output, Comment.
  • Custom nodes created through the Command Builder are listed last.

File Write Log appears under Utility — appending a timestamped audit line is a general-purpose step, not a file-management one. It is also available as a Script Builder command under the File category.


Q. Does the Node Editor support multi-user concurrent editing?

A. Yes. The Node Editor uses a server-side graph lock to prevent two users from editing the same job at the same time. When you open a graph for editing, the server grants you an exclusive lock. If another user tries to open the same graph/job while it is locked, they are informed and can't edit. The lock is automatically released when you save and close, or when your connection drops — so no graph ever gets permanently stuck.


Q. What is the Command Builder in the Node Editor?

A. The Command Builder is a palette section inside the Node Editor that allows you to visually construct any ACE Resolver command by selecting from a searchable list of all available commands and filling in their parameters.


Q. How do I check the current UTC time in the Node Editor?

A. Use the UTC Time Check Node in the Expression category. It has only three inputs:

  • Unit: Seconds, Minutes, Hours, Days, Months, or Years
  • Operator: ==, !=, <, >, <=, or >=
  • Expected: the value you want to compare against

For example, Hours > 18 evaluates whether the current UTC hour is greater than 18. Internally ACE compiles it to COMPARE{NUM;>;CMD{!time utc %h},18}, returning 1 for true and 0 for false. The check always uses UTC, not the local time zone of the Server or Client.

The existing TimeCheck node is still available when you need custom time utc format specifiers or an offset, and existing graphs using it are unchanged.


14. Chat System


Q. What is the ACE built-in chat system?

A. ACE includes an integrated, encrypted chat system with two conversation types:

  • Direct Messages (DM) — private one-to-one conversations between any two users.
  • Group Chat — multi-user conversations tied to Permission Groups. Every PermGroup has a group chat.

Q. Is chat history stored on server's disk? Is it encrypted?

A. Yes to both. Chat messages are encrypted & stored on the server's disk.


Q. Can a user in one environment chat with a user in another environment?

A. Yes.


15. Security & Encryption


Q. How does ACE encrypt data at rest?

A. ACE uses AES encryption for all persistent data stored on disk.


Q. How does ACE encrypt data in transit?

A. All communication is encrypted with SSL/TLS (TLS 1.2 and TLS 1.3).


Q. What DDoS protection does ACE provide?

A. The ACE server includes a built-in per-connection rate limiter that enforces a configurable maximum requests-per-second. The default is 10 requests/second per connection.

When a connection exceeds the rate limit, the excess requests are dropped.


16. Networking Internals


Q. How does ACE handle concurrent connections?

A. ACE uses dedicated thread pools for I/O and request processing:

  • Connection threads (system set-connection-threads <n>) — handle the raw I/O of accepting and reading from sockets.
  • Request threads (system set-request-threads <n>) — process incoming parsed requests (Resolver command dispatch). Separating I/O and processing prevents slow commands from blocking new connections.
  • Client threads (system set-client-threads <n>) — set a number of threads on client's machine - processing incoming requests from server on client.

Thread counts are tunable at runtime via GUI/CLI and persist across restarts.


Q. What is server-to-server redirection?

A. Multiple ACE Local Server instances can redirect messages and requests between each other. One-to-many forwarding is supported: a single source server can fan out to multiple destination servers simultaneously.

Two servers only link when they belong to the same customer — the account the licences were bought under. A server that holds licences for several customers keeps them apart: every redirected message travels with the customer it belongs to, and a server acts on it only for that customer's environments.

system set-redirect-servers <server_ip1;server_ip2;server_ip3;...>
system set-redirect-servers EMPTY          # unlink all
system list-redirect-servers               # which servers are linked right now

The first command sets which servers this one should connect to. The third shows which ones it is actually connected to at this moment — servers this one dialled and servers that dialled it, along with the environments the two machines have in common.

The two lists are not the same thing, and the difference is usually the answer when a cross-server message or request seems to do nothing: a server can be configured but not linked because it is switched off, unreachable, running a different version, or belongs to a different customer. In the GUI the same live list is shown in the redirect-servers window, refreshing on its own while the window is open.

If a link is refused, the server log names the reason — a version mismatch or no shared customer between the two machines.


A. A link is one-way. It carries messages and requests only from the server that dialled it to the server that accepted it. If only machine A lists machine B in system set-redirect-servers, then A can send to B and B cannot send to A. That is by construction, not a fault: the dialling side never reads that socket again after the handshake, so anything written back down it would never be picked up.

For traffic in both directions, each server must list the other — that opens two links, one per direction.

system list-redirect-servers reports one row per peer, with a direction of:

Direction What it means
outgoing This server dialled the peer. You can send to them; they cannot send to you.
incoming The peer dialled this server. They can send to you; you cannot send to them.
bidirectional Both links exist. Messages and requests flow both ways.

So two machines that show incoming on one side and outgoing on the other are not bidirectional — they are a single one-way link, and exactly one direction will work. Add the missing target on the other machine and both rows become bidirectional.

When at least one peer is one-way, the listing prints a reminder of this underneath.


Q. Why does a linked server show no shared environments?

A. Two different reasons, and the listing distinguishes them.

Only the side that receives on a link is told the peer's environments: the peer announces them right after the handshake, and the dialling side does not read the link. So on an outgoing-only row the column says not reported — the peer's environments are unknown here, which is not the same as "the peer shares nothing". Linking the other direction as well makes the list appear.

When the environments are known, the column lists only those belonging to a customer this server also holds — the ones a redirect from here could actually be delivered into. A peer may hold customers this machine does not; those environments are deliberately not counted as shared, because nothing addressed to them could ever arrive.


Q. Both servers list each other, so why is traffic still one-way?

A. Because listing a peer and being linked to it are two different things. Each server has to be able to dial the other, which means the ACE port must be reachable inbound on both machines — a host firewall on one of them is the usual cause, along with the peer being switched off, the two servers running different major.minor versions, or the two holding no customer in common.

Run system list-redirect-servers on both machines and compare:

Address: 10.0.0.15 | Direction: bidirectional | ...        both links are up
Address: 10.0.0.15 | Direction: outgoing | ...             we can send; they cannot
Address: 10.0.0.15 | Direction: incoming | ...             they can send; we cannot
Address: 10.0.0.15 | Direction: configured, not linked     we list them, our dial fails

configured, not linked is the one that answers this question: that side's outgoing half never came up, and the reason was written to its log when the link was attempted. Fix that and both rows turn into bidirectional.

You do not have to re-run anything afterwards. A configured target with no outgoing link is re-dialled about every 30 seconds, so once the cause is gone — the peer boots, the firewall rule lands, the versions match — the link comes up on its own. A target that already has a live link is never re-dialled, so this cannot produce a duplicate link, and a refusal that will not resolve by itself is logged once instead of twice a minute forever.

The four reasons a dial is refused, in the order they are worth checking:

  1. The peer is unreachable — switched off, or its ACE port is blocked for incoming connections. A host firewall on the peer is the usual one.
  2. Different versions — the two servers must agree on major.minor.
  3. No shared customer — environment names are unique only within a customer, so ACE refuses to link two servers that hold none in common rather than resolve someone@ace against a different customer's ace.
  4. The peer does not list you — then only your half exists, and the row reads outgoing. That is not a failure; it is a configuration choice on their side.

Q. Can a remote request run a command on the other server itself?

A. No, and this is deliberate.

system execute-on-client and system resolver-execute-on-client reach clients — the machines logged in to a server — and never the server host itself. That does not change when the caller is on a linked redirect server: a redirected request is delivered to the addressed user's client sessions, exactly as a local one would be. There is no command that makes one server run an OS command on another server's own machine, and none is planned.

The reason is that a link is a trust boundary between two administrators. Granting a peer the ability to run a shell on your host would hand the administrator on the other side full control of your machine, and nothing about a shared customer implies that. The server-side system execute is therefore a purely local command, and its Execute privilege is local too.


Q. How do I authorise somebody who lives on a linked server?

A. Two grants exist, and which one you want depends on scope.

Per user (the target grants the caller):

user add-remote-request-privilege <local_user> <their_user>@<their_environment>

This lets that one remote identity send requests to that one local user.

Server-wide (an administrator grants the caller):

system add-privilege ExecuteOnClient <their_environment> <their_user>

Note the argument order — the privilege, then the environment, then the user. <their_environment> is the environment name as it is used on the peer, and it does not have to exist on this server: ExecuteOnClient is the one server-wide privilege that may name a remote environment, because it is the only one a redirected caller can ever be checked against.

The grant is stored as the trusted identity user@environment and compared against the identity a redirected request carries, so nothing is verified locally — the administrator vouches for it, and a typo simply never matches. A permission group on the peer cannot be used here: its membership is only known on that machine, so grant the individual users instead.

Which server-wide privileges can name a remote identity? Exactly one:

Privilege Cross-server
ExecuteOnClient Yes
Execute No — runs an OS command on this server, which a redirected request can never reach
Sleep No — this server's own session state
AddAlias, RemoveAlias, ListAliases No — this server's own alias state

That is not a policy choice: the other five are only ever consulted for a caller on this machine, so a remote grant for them could never match anything.

In the GUI: System toolbar → Manage Privileges. Type the target as user@environment (the same notation as everywhere else); the dialog puts the halves in the resolver's order for you. The line under the field tells you what the selected privilege accepts, and picking one of the other five with a foreign environment is refused there rather than coming back as GetEnvironmentByName.

Every other server-wide privilege (Execute, Sleep, AddAlias, RemoveAlias, ListAliases) acts on this server alone and is rejected for an environment this server does not host.

system remove-privilege takes the same arguments and removes the grant again; system view ExecuteOnClient lists local and cross-server grants together.


Q. Who can send to whom across two linked servers?

A. Anyone on one server can address anyone on the other, as long as both servers belong to the same customer. The rule is about the customer, not about the environment or the user name.

A message is addressed the same way whether the recipient is local or on another server:

system send <recipient>@<environment> <text>

What happens with it:

Part of the delivery What it means
Sender's user name The name the sender is logged in with on their own server. It travels with the message and is what the recipient sees as the sender. No account of that name is needed on the receiving server.
Sender's environment The environment the sender is logged into, as named on their own server. It does not have to exist on the receiving server.
Sender's customer The account both servers were licensed under. It travels with every message and request, and the receiving server checks it. This is the one thing that must match.
Recipient's user name The name to deliver to, exactly as it exists on the receiving server.
Recipient's environment The environment on the receiving server that holds the recipient. Use the name it has there.
Recipient's customer Must be the same customer as the sender's. An environment of the same name belonging to a different customer is never a recipient.

The sender keeps their own identity throughout — they are shown as name@their-environment, and a user of that same name on the receiving server is treated as a different person, with no rights borrowed from them. For anything beyond a plain message (running a command on someone's machine, for example), the recipient has to grant that sender explicitly, by the name@environment they use on their own server.


Q. My two servers use different environment names. Will messages between them still arrive?

A. Yes. The two servers are peers, not copies of each other — the same customer can name environments differently on each machine, and nothing has to be renamed to match.

Worked example, one customer with two servers:

Server 1 Server 2
Environment ace eca
User tester1 tester2

tester1, logged into ace on server 1, sends:

system send tester2@eca hello
  • Server 1 has no eca, so it hands the message to the linked servers of its own customer.
  • Server 2 accepts it, recognises tester1 as the sender even though it has no ace, and delivers hello to tester2 in eca.
  • tester2 sees the message from tester1; the reply travels the same way in reverse.

The same applies to remote command execution and to the other redirected operations — the recipient is always addressed by the environment name used on the server that hosts them.


Q. Do the servers know what the other one has?

A. Yes. As soon as two servers link, each tells the other which environments it holds — and tells it again whenever that changes, so neither side ever works from a stale picture. Nothing has to be refreshed by hand.

That has two practical effects:

  • A message is only carried to a server that can actually deliver it, instead of being offered to every linked server and discarded by the ones it does not concern.
  • A server only ever learns about environments belonging to a customer it already shares. It is told nothing about the rest.

A server that has just linked and has not announced yet is still sent everything, so nothing is lost in the moment between connecting and comparing notes.


Q. Can one server hold environments for more than one customer?

A. Yes. Each environment is licensed on its own and belongs to the customer it was bought under, so a single machine can host several customers side by side.

They are kept strictly apart. A message or request always carries the customer it belongs to, and the receiving server acts on it only within that customer's environments — never another's, even on the same machine, and even when the two use the same environment name. A linked server can only address customers it proved it shares when the link was made.


Q. What if both servers have an environment with the same name?

A. Then the name alone would be ambiguous, and ACE resolves it by customer rather than by name:

  • Two servers of the same customer, both with an environment called main: a message addressed to someone@main is delivered to main on whichever server holds that user, and both servers treat main as the same customer's environment.
  • Two servers of different customers that happen to both use main: they never link in the first place, and even if one machine hosts environments for both customers, a message scoped to one customer is never delivered into the other customer's main.

Broadcasts follow the same rule: a message sent to everyone reaches the sender's customer's environments on every linked server, and nobody else's.


Q. A message to a user on another server was not delivered. Where do I look?

A. Run system list-redirect-servers first — it shows which servers are linked right now, which is the one thing that has to be true before anything can be delivered. If the other server is not in that list, nothing else matters yet. The receiving server also logs why it dropped a redirected message, so check its log next. The usual causes, in order of likelihood:

  1. The two servers are not linked — system list-redirect-servers does not show the other machine. Check system set-redirect-servers on both and the startup log for a refused link.
  2. The recipient's environment name is the one used on the sending server. Address the recipient with the environment name used on the server that hosts them.
  3. The recipient does not exist on any linked server under that name.
  4. The servers belong to different customers. Cross-customer delivery is not possible by design.
  5. The two servers run different versions. Run update separately on both servers.

Q. A command sent to a user on another server did nothing. Why?

A. Sending a message and running a command on someone else's machine are governed differently. Anyone may message anyone on a linked server; running something on another person's machine additionally requires that person's server to allow you specifically.

Permissions are never inherited across servers. Being an administrator on your own server grants you nothing on another one — an administrator of the recipient's server has to allow your account there, by name, for that user. This is deliberate: two people can share a user name on two machines without being the same person.

Whether the request is carried out is decided on the other machine, and that machine's log is where the answer is. If it refused the request, its log names your account and the user you addressed, so an administrator there can see exactly what to allow.

Two more things worth checking before anything else:

  • The link runs the right way. A link only carries traffic from the server that dialled it. If system list-redirect-servers shows the peer as incoming on your machine, your requests cannot reach it at all; the other side has to list you too.
  • The command reaches clients, never the other server. system execute-on-client runs on the addressed user's client machines. If that user has no client session open on the peer right now, there is nothing to run it on, and the peer's log says so.

Q. How does the ACE server limit the total number of connections?

A. A global maximum connection limit is enforced at the server level:

system set-max-connection-limit <number>

When the limit is reached, new connection attempts are refused at the socket-accept layer. The default limit varies by edition. Per-user active connections can be listed and individual connections can be forcibly disconnected either via GUI or CLI.


Q. How does ACE handle high network throughput?

A. ACE's network layer is designed for high concurrency and efficiency. The server uses non-blocking, asynchronous I/O so that processing many simultaneous connections adds minimal overhead. I/O threads and Resolver dispatch threads operate independently, preventing slow commands from blocking new connections. Thread counts are tunable at runtime via GUI/CLI and persist across restarts.


Q. How does ACE handle client disconnects gracefully?

A. When a client disconnects (clean close or network drop):

  1. All HYBRID timers that were running on that client are automatically handed off to the server (phase-preserving).
  2. All graph locks (node editor) held by that connection are automatically released — no stuck locks after a crash.
  3. If configured, the user's OnDisconnect event command fires automatically.

17. Editions & Pricing


Q. What editions of ACE are available?

A. https://www.ace-software.net/editions


Q. What hardware features require the Standard edition or higher?

A. The Standard edition is required for all hardware-control features:

  • Controllers — Arduino (Uno, Mega2560), Raspberry Pi (GPIO)
  • Sensors — RFID readers, buttons, analog sensors
  • Devices — relays, LEDs, dimmable lights, ...
  • Serial Ports — full serial communication management
  • Permission Groups

For a full list, visit: https://www.ace-software.net/editions


Q. How does ACE licensing work?

A. Each environment requires a valid license key tied to the selected edition. Expired environments are locked until reactivated. Activation and reactivation require internet access to the license server.


Q. How is payment handled for ACE?

A. Payments are processed via Stripe — a PCI-DSS compliant payment processor. ACE provides a customer portal for account management (subscription upgrades/downgrades, billing history, payment method updates). There is no ACE-side storage of payment card data.


Q. Is there a free trial?

A. Please check the official ACE website ace-software.net for current trial or demo options, as these may be updated over time. The editions page at ace-software.net/editions always reflects the latest pricing and trial availability.


Q. How do I request a refund for an ACE payment?

A. Sign in to the Customer Portal and choose Request Refund, or open https://ace-software.net/refund while signed in. Select the exact Environment and paid invoice you want reviewed, choose a reason, and submit the request. Every request is reviewed manually; submitting it does not itself issue a refund, cancel a Subscription, or disable a Licence.

For an applicable EU/EEA consumer right of withdrawal, use the separate https://ace-software.net/withdrawal form. For a defect, non-conformity, or another statutory consumer claim, contact Support. These routes are intentionally separate because a voluntary refund request is not the same legal process as withdrawal or a statutory remedy.


18. Deployment & Installation


Q. How do I run ACE as a background service?

A. On Linux, ACE can be registered as a systemd service so it starts automatically at boot and restarts on crash. The service file configures the server binary with the appropriate data directory. On Windows, ACE can be registered as a Windows Service for similar always-on behavior.


Q. Can I run multiple ACE environments on one server?

A. Yes. Multiple environments can be active simultaneously on the same ACE server instance. Each environment:

  • Has its own license key (requires a separate license)
  • Has its own isolated data directory, users, and components
  • Runs in the same server process but is completely isolated at the object level

This makes ACE suitable for hosting multiple tenants or multiple organizational units on a single physical machine.


Q. How do I update ACE?

A. ACE has a built-in update system:

GUI: - System panel -> Check Update & Update buttons CLI:

check-update         # check if a newer version is available (returns 1/0)
update               # download and install the latest version

Client binaries are automatically updated when they connect to a server running a newer version — the server pushes the updated client binary to the connecting client. This ensures all clients in a deployment stay in sync with the server version without manual distribution.

The update is downloaded while the server keeps running. If there is nothing to update, the server simply keeps running — it is not restarted and no Client is disconnected. If the download is incomplete, nothing is installed and the server keeps running. Only a complete update restarts the server, and every logged-in Client then reconnects on its own (see What happens to connected Clients when the Server is updated? below).


Q. How do I access ACE from outside my home/office network?

A. Three common approaches:

  • Port forwarding — forward port 4542 and optionally 4541 (web dashboard) on your router to the server's internal IP. Clients connect using your public IP or a dynamic DNS hostname. Suitable for private use; use IP whitelisting for security.
  • VPN — put your server behind a VPN. Clients connect to the VPN first, then ACE appears as a local network service. Most secure option for remote access.

19. Scripting Engine (.asf Files)


Q. What is an ACE Script File (.asf)?

A. An ACE Script File (.asf — ACE Script File) is a plain-text file containing structured ACE Resolver commands organized into named sections. Scripts are the primary way to encode complex, multi-step automation workflows that go beyond what a single Command's jobs can express.

Every .asf file must begin with the #ACE header line. The #ACE header is automatically prepended by the Script Builder compiler.


Q. What section types exist in an .asf script?

A.

Section Keyword Behavior
#run Main entry point. Executes synchronously on script start.
#section <name> Named synchronous section. Blocks until all commands in it finish. Called via JOIN <name>.
#async_section <name> Named asynchronous section. Runs in a separate thread while the caller continues. Called via EXEC <name>.
#full_async_section <name> Fully fire-and-forget. Spawned in a background thread; the caller never waits. Called via EXEC <name>.

Sections allow scripts to express both sequential and concurrent workflows.


Q. How do I declare and use variables in an .asf script?

A. Script-local variables are declared with #var and manipulated with dedicated script directives:

#var counter 0          # declare variable "counter" with initial value "0"
#setvar counter 5       # assign a new value
#incrementvar counter   # counter = counter + 1
#decrementvar counter   # counter = counter - 1

Script variables are local to the script execution — they do not persist after the script finishes and are not ACE component Variables. They are compiled first (before section nodes) and ordered by Y-position on the Script Builder canvas.


Q. How do I run an .asf script?

A. Use env load to execute a script:

# Run a script directly:
env load /path/to/myscript.asf

# Run as a specific user (the script executes under that user's privileges):
env load /path/to/myscript.asf --user alice

# Skip pre-validation (use only when syntax is pre-verified):
env load /path/to/myscript.asf --no-check

To trigger a script on a schedule or event, create a Command whose job task string is env load /path/to/myscript.asf and attach it to a Timer or VarExpression. This allows fully automated, timer-driven or event-driven script execution.

The Node Editor Script Builder mode compiles the graph directly to an .asf file on the server at <ACE_DATA_DIR>/graphs/<uuid>.asf. That path is what you pass to env load.

When env load or env loadable is entered from a remote Client, the Client reads the local .asf file and transfers that script payload to the Server. The Server stores the received script in its scripts directory and runs the load/loadable check there, while routing command output back to the requesting Client connection.


Q. What is a Virtual Environment in the scripting context?

A. Before any .asf script is executed against a real (production) environment, the Resolver validates the script against a Virtual Environment before it is allowed to affect production data. The virtual environment is an in-memory clone of the real environment, created specifically for the validation run.

If validation succeeds (no syntax errors, no missing component references), execution proceeds on the real environment. If validation fails, the error is reported and the real environment is never touched — providing a safe "dry run" for all scripts.

Virtual environments are isolated so multiple scripts can be validated concurrently without naming collisions.


Q. Are scripts in ACE thread-safe?

A. Yes. Script executions within the same environment are serialized, preventing two scripts from running concurrently and causing unpredictable state interactions. A script can also call another script safely, without any risk of the two blocking each other.


Q. Can a script pass user context when executing?

A. Yes. The env load command supports the --user <username> option, which loads the environment as a specific user. All subsequent operations in the loading sequence (and any scripts triggered by AfterLoad events) run under that user's identity and are subject to their privileges.

For scripts run from jobs, the executing user is the user who triggered the command, so permissions and ownership remain tied to that user context.


Q. How do I call one section from another inside an .asf script?

A.

JOIN <section_name>    # call a synchronous section (blocks until it finishes)
EXEC <section_name>    # spawn an async or full-async section (returns immediately)

Synchronous sections (#section) called via JOIN behave like function calls — the script waits for the entire section to complete before continuing. Async sections (#async_section) called via EXEC run in parallel with the calling section. #full_async_section is fully detached — the caller never waits and never checks completion.


Q. Are .acegraph and .asf files portable between environments?

A. Yes. Both file formats are designed to be portable:

  • .acegraph — contains the visual node layout in JSON. When imported in a new environment, the Script Builder detects a "new environment" state and triggers a full first-deployment, re-creating all server-side infrastructure (commands, timers, etc.) from scratch.
  • .asf — contains pure ACE Resolver commands as text. Use Current User and Current Environment nodes in the Script Builder to avoid hardcoding user or environment names. These special nodes are substituted with the actual current user and environment name at runtime.

20. Polls & Voting


Q. What is the poll/voting system in ACE?

A. ACE includes a built-in binary or multiple-choice voting system available from the ACE command interface and GUI. Polls allow users to vote on options, with results tracked in real time and optional automation triggered when a winning option is determined.


Q. Can a Poll fire a Command when it concludes?

A. Yes. Use poll add-win-command to attach a Command to a specific winning outcome:

poll add-win-command <poll_name> <option> <username> <db> <command_name>

When poll close is called (or the poll expires via poll expires), the winning option's attached command fires. This enables automated decisions based on group voting — e.g., a team vote on whether to restart a service triggers the restart command automatically.

poll remove-win-command <poll_name> <option>
poll expires <poll_name> <duration>   # set automatic expiry (e.g. 30s, 5m, 1h)

This can be achieved through the GUI (Right panel -> "Voting / Polls" button) or CLI.


Q. How do I remove a poll?

A.

GUI: - Right panel -> "Voting / Polls" button CLI: - poll remove <poll_name>

This removes the poll and all its vote history. Win commands are also detached. If the poll is still open when removed, no win command fires — it is a silent removal.


Q. Can polls be used in automation workflows?

A. Yes. Polls can be created, voted on, and closed entirely via Resolver commands — meaning all three steps can be job tasks inside Commands. A timer could periodically open a new poll (via poll create), collect votes from users over a defined window, close it (poll close), and trigger automation based on the outcome — all without any manual intervention.


21. System Commands & Process Management


Q. What does system execute do?

A. system execute <command_and_args> runs an OS-level command on the server. The command is executed in the server's process context with the server's OS user permissions.

system execute ls /var/log          # Linux: list directory
system execute "cmd /c dir C:"    # Windows: list C drive

Q. What does system execute-on-client do?

A. system execute-on-client <user@env> <command_and_args> runs an OS-level command on the client machine of the specified user. This is a powerful remote-execution feature that allows the server to orchestrate actions on connected client machines.

system execute-on-client alice@env1 "shutdown /s /t 0"   # shut down the client PC
system execute-on-client alice@env1 "rm -rf /home/alice/Documents/Junk"

Q. What does system resolver-execute-on-client do?

A. system resolver-execute-on-client <user@env> <command> sends a Resolver command string to be executed on the client side of the specified user — not as an OS process but as an ACE Resolver command.


Q. How do I ban a process for a user?

A.

GUI: - Click Server button, User, or PermGroup (component tree) and click the "Manage Banned Processes" button in the top bar. CLI: - process ban <process_name> '<username@env>' - process unban <process_name> '<username@env>' - process list '<username>' - process list '#permgroup@env' - process list '#permgroup' (uses current environment)


Q. What is system send?

A. system send <target_user> <message> sends a message to one or more users (including permission-group and broadcast targets) and stores it in server-side chat history for resolved recipients, so offline recipients can read it later.

GUI: - Right panel -> Open Conversations CLI: - system send alice@env1 "Hi Alice!"


22. Serial Ports (Advanced)


Q. What is auto-baud scanning on SerialPorts?

A. serialport set-scan-bauds <username> <db> <sp_name> <0/1> enables automatic baud-rate detection. When enabled, ACE tries all standard baud rates in sequence when linking with the serial port (ACE Serial Port object & real hardware serial port).


Q. What does serialport write-raw do?

A. serialport write-raw <username> <db> <sp_name> <data> sends arbitrary raw bytes directly to the serial port without any ACE framing or protocol wrapping. This is useful for debugging, sending manual commands to hardware, or implementing custom serial protocols that ACE doesn't natively support.


Q. What is serialport reconnect?

A. serialport reconnect <username> <db> <sp_name> closes and re-opens the serial port connection. ACE also monitors for USB serial device arrival/removal events and can reconnect automatically.


Q. How do I check which serial ports are actually Online?

A. Run serialport list-online. It lists every serial port visible to you (the same visibility scope as the component tree) together with its current status — Online or Disconnected. The command is always scoped to the logged-in user and takes no arguments.

The status reflects whether the port is actually open right now: the server unions the open-port reports from every client connection in the environment with its own locally-linked handle, so a port shows Online if any connection currently holds it open. In the GUI, the Serial Port Status dialog presents the same information and refreshes automatically about once per second.


23. Update System & Multi-Language


Q. How does the automatic client update work?

A. When a client connects to an ACE server running a newer software version, the server automatically pushes the updated client binary to the connecting client. The client binary is replaced transparently and the client restarts on the new version. This means:

  • Zero-touch deployment — no need to manually update every client machine.
  • Multi-architecture support — the server stores both x86_64 and aarch64 binaries and selects the correct one based on the connecting client's architecture.

Q. What happens to connected Clients when the Server is updated?

A. They reconnect automatically — no manual reconnect on each Client is needed, which is especially handy for Raspberry Pi Clients in another room.

When update has downloaded and verified a new version, the server tells every logged-in Client (CLI and GUI) that it is restarting into an update, right before it restarts. Each Client then:

  1. Shows the message "The server is being updated. This client will reconnect automatically - no action is needed." (in the Client's language), with a countdown — a small Reconnecting to the server window in the GUI, status lines in the CLI.
  2. Waits the reconnect delay (default 60 s), plus a small random spread so many Clients do not reconnect in the same second.
  3. Logs back in on its own. The server hands each Client a one-time credential for this — the password is never stored or re-sent — and the login is exactly a normal login: devices, sensors, RFID readers, buttons, timers and events are restored as after any login.
  4. If the server now runs a newer version, the Client updates itself first, restarts, and continues reconnecting automatically.

While the server is still starting, the Client retries after 5, 10, 20 and then every 30 seconds, until the reconnect window (default 15 minutes) ends. If reconnecting is not possible (for example the user was blacklisted meanwhile), the Client returns to the normal login. In the GUI, Reconnect now skips the countdown and Cancel returns to the login form. A Client started with --autologin-persist keeps trying with its stored credentials after the window.

Both values are set by an administrator:

GUI: - System panel → Reconnect delay (s) / Reconnect window (s) → Set CLI:

system set-reconnect-delay 60      # 10-600 seconds, default 60
system set-reconnect-window 900    # 60-3600 seconds, default 900
system view ReconnectDelay
system view ReconnectWindow

A normal exit / shutdown of the server does not trigger automatic reconnects — Clients return to the login form as before. This works from the second update onwards: the server that performs the update must already contain this feature.


Q. How does multi-language support work in ACE?

A. ACE supports five languages natively at the engine level:

Code Language
EN English (default)
SK Slovak
CZ Czech
RU Russian
DE German

All CLI command descriptions, error messages, syntax strings, and UI labels are stored in multiple languages.

The language is set via: GUI: - System button (under the component tree) and "Language" CLI: - system set-language <EN/SK/CZ/RU/DE>


Q. Are GUI translations and CLI translations the same?

A. Both the GUI and CLI use the same messages — changing the language setting updates all strings, which feed both the CLI and the GUI.


24. Use Cases


Q. What are the top use cases for ACE in home automation?

A.

  • RFID access control — RFID door locks that log entry/exit to variables and trigger commands
  • Smart lighting — relay/LED control via timers or sensor triggers
  • Motion-triggered scenes — button press fires a command that dims lights, starts a timer, sends a webhook notification
  • Temperature monitoring — sensor → variable → expression chain alerts when temperature threshold is crossed
  • Automated watering — timer-driven relay control for irrigation systems
  • Home theater — button scenes: dim lights, lower projector screen, power on amplifier relay

Q. What are the top use cases for ACE in IT/office environments?

A.

  • Scheduled process management — timer-driven scripts that launch or kill processes on schedule
  • Remote configuration deployment — server pushes config file updates to client machines via file commands
  • Multi-PC control — ban unauthorized processes across a fleet of machines
  • Script-based patch deployment — .asf easily shareable scripts that perform multiple actions
  • System health monitoring — variables tracking CPU/memory metrics with expressions triggering alerts
  • Remote shutdown/restart — system execute-on-client <user@env> "shutdown /r /t 0" from the server

Q. What are the top use cases for ACE in industrial/agricultural settings?

A.

  • Greenhouse automation — temperature/humidity sensors feeding variables → expressions triggering ventilation relays and heating elements
  • Irrigation control — timer-driven relay sequences with soil moisture sensor feedback
  • Cold-chain monitoring — refrigeration temperature logging to variables with webhook alerts when thresholds are crossed
  • Machine scheduling — timer-based startup/shutdown sequences for production equipment via relays
  • RFID staff tracking — employee badge scans logged, triggering clock-in/clock-out commands

Q. What are the top use cases for ACE in security applications?

A.

  • RFID door access — authorized tag list with per-tag commands (open/close relay), unauthorized tag alerts via webhook or system-send
  • Panic button — button sensor triggers an immediate webhook notification to security team
  • Multi-zone lockdown — a single command fires multiple relay-controlled locks simultaneously
  • Audit trail — all RFID scans and button presses logged to file via job tasks
  • Process lockdown — ban unauthorized processes on exam/kiosk machines remotely

Q. Can ACE be used for data collection and logging?

A. Yes. A common data-collection pattern in ACE:

  1. Sensor reads value → stored in a Variable via sensor add-variable
  2. Variable expression triggers (e.g. on every value change) → fires a Command
  3. Command job appends the value and timestamp to a log file
  4. Periodic webhook sends a batch of readings to an external API or database

This creates a complete sensor → storage → notification pipeline with no additional software.


Q. Is ACE suitable for multi-tenant SaaS deployments?

A. ACE's environment isolation makes it viable for multi-tenant setups on a single server. Each tenant gets their own environment with: - Completely isolated users, data, and automation - Separate license keys and editions (can have different tiers per environment)

For large-scale multi-tenant deployments, use server-to-server redirection to distribute load across multiple physical machines, with each machine hosting a subset of tenant environments.


25. Troubleshooting


Q. Microsoft Defender flags the Installer as Trojan:Script/Wacatac.B!ml. Is ACE infected?

A. No. Wacatac.B!ml is a generic machine-learning verdict, not a signature for any specific malware — the !ml suffix means the file was flagged by a prediction model rather than matched against known malware. Nothing was found inside ACE: the model scored the file's shape and delivery, not its contents. It is one of the most common false positives for independently distributed Windows software.

Why it appears on one machine and not another:

  • Prevalence. Defender weighs how many machines worldwide have already seen a given file. A newly published release that only a handful of people have downloaded scores as unfamiliar by default. That is why the flag can appear on a colleague's computer but not on yours, and why it often shows up for a new version and then stops on its own as more people install it.
  • Compressed executables. ACE binaries are compressed so the download stays small. Compression is common in legitimate software but also used to make files harder to scan, so prediction models weigh it.
  • An installer that downloads and writes programs. That is exactly what an installer does, and it is also the shape of an unwanted downloader — the model cannot tell the two apart from the file alone.

What to do:

  1. Verify the download. Every release publishes a SHA-256 checksum. If the checksum of your downloaded file matches the published one, you have the genuine, unmodified file.
  2. Download only from the official site over HTTPS. A file obtained anywhere else should not be trusted regardless of what any scanner says.
  3. Allow the file in Defender once the checksum matches, or wait — a verdict driven by unfamiliarity usually clears by itself as the release becomes more widely installed.
  4. Report it to us if you hit this. We submit affected builds to Microsoft for re-evaluation, which clears that exact file for everyone. Because each release is a new file, the verdict can reappear on a later version even after an earlier one was cleared.

If you would rather confirm independently, upload the installer to a multi-engine scanning service: a genuine infection is flagged by many engines, while a prediction-model false positive typically shows up in only one or two.


Q. My timer is configured but not firing. What should I check?

A. Work through this checklist:

  1. Is the timer started?
  2. Is a command attached?
  3. Is the interval correct?
  4. Are repeats set? — if repeats = 0, the timer fires once and stops. Set to -1 for infinite.
  5. CLIENT or HYBRID mode without a connected client? — CLIENT mode requires an active client connection. Switch to SERVER or HYBRID if no client is always available.

Q. A command job is failing silently. How do I debug it?

A.

  • Run the job's task string directly via the CLI terminal and look at the output
  • Add file write-log calls around the failing step, then re-run and read the log back with file view-log <path> <lines>. A silent failure is silent because nothing recorded the inputs — logging the values the job actually saw usually identifies the cause on the first re-run. See How do I record what my automation is actually doing?

Set this up before you need it. Logging added after an intermittent fault appears only helps on the next occurrence, which may be days away. Environments that write their own logs from the start diagnose problems in minutes rather than waiting to reproduce them.


Q. My hardware controller stopped responding.

A.

  1. Try to physically reconnect the controller
  2. Try to reconnect the Serial Port (via "Reconnect" button (after clicking on a Serial Port object from a component tree) or "serialport reconnect ")
  3. Check the baud rate of Serial Port
  4. Try reuploading the firmware in case of an Arduino ("controller upload ...")

Q. The ACE server won't start. What should I check?

A.

  • Make sure you launch the program via a Launcher
  • Make sure you launch the program (the Launcher) with administrator privileges
  • Check startup output (even inside of the CLI version of ACE)

Q. How do I check the ACE version?

A.

Type this command into ACE's terminal: version



Q. What format do whitelist and blacklist commands use for users?

A. Use a single user@environment target. For example:

system add-to-whitelist user1@env1
system remove-from-whitelist user1@env1
system add-to-blacklist user2@env2
system remove-from-blacklist user2@env2

The CLI, GUI-generated commands, docs, and examples use the single user@environment form.

Q. A webhook call is failing. How do I diagnose it?

A.

# Test synchronously to see the HTTP response and error body:
webhook call-sync <webhook_name> 30

# Inspect current configuration:
webhook info <webhook_name>

# Common issues:
# - Wrong URL:         webhook target <webhook_name> <correct_url>
# - Missing auth:      webhook header set <webhook_name> Authorization "Bearer <token>"
# - Expired token:     re-run the OAuth2 setup flow
# - Firewall:          test outbound connectivity from the server with curl

If issues persist, try following the official documentation of the target service.

Q. How do I list permission-groups for a user?

A. Run:

user list-permgroups <user_name>

Allowed only for: - admin, - the same user (self), - permgroup manager.

In GUI use User toolbar → List PermGroups; output appears in the bottom terminal log.

Q. How does Manage Banned Processes routing work?

A. The dialog has 3 dedicated tabs and strict targets:

  • Server tab (formerly System): process ban/unban/list <process> (server-local process monitor scope).
  • User tab: process ban/unban/list <process> <user@env>.
  • PermGroups tab: process ban/unban/list <process> #<group@env>.

Default opened tab follows caller context (System/User/PermGroup toolbar origin).

Q. Does process ban <proc> (without recipients) ban it for all users?

A. No. In current engine behavior, recipient-less process ban <proc> updates the server-local process monitor scope only. It does not automatically copy that process into every user's banned-process list.
Use explicit recipients (user@env, all@env, #group@env, etc.) when you want user/group-targeted bans.

Q. Does process ban notepad.exe #group create one shared group process list?

A. No. Current engine behavior applies the ban to users resolved by the group target. Internally, bans are stored on users, so process list #group is rendered as per-user sections (user: + that user's banned processes). The Manage Banned Processes PermGroup tab aggregates these into a deduplicated process-only view for convenience.

How do I set severity and UTC time for a custom log entry?

Use file write-log <INFO|WARNING|ERROR> <path> [--no-time] <content...>. The level is mandatory. logs/ targets the active environment's ACE logs directory. UTC time is included by default; add --no-time immediately after the path to omit it.


26. GUI Controls


Q. What keyboard shortcuts and tree actions are available in the ACE GUI?

A. The MainWindow provides several shortcuts for actions that already exist in the GUI:

  • Ctrl+F focuses the search field above the component tree. The search filters the current tree case-insensitively.
  • Esc clears the tree search while a filter is active and restores the full component tree. If the filter is already empty, Esc keeps its normal GUI behavior.
  • Ctrl+D opens the existing Duplicate dialog when duplication is available for the selected component.
  • Ctrl+S performs the same action as the current toolbar Save button when saving is available.
  • Delete asks for confirmation and then performs the same action as the current toolbar Remove button. Delete keeps its normal editing behavior while an editable text field has focus.
  • Normal text editing shortcuts such as Ctrl+A, Ctrl+C, and Ctrl+V continue to work in GUI text fields.

A double-click on a Command object selects its normal Command panel and immediately opens the existing Manage Jobs dialog. The shortcut is based on the object's component type rather than its translated display text.

Right-clicking any tree component that has both a name and UID provides Copy Name and Copy UID. Additional object-specific shortcuts are available:

  • Device / Relay / Light / Led: Toggle Value, Turn On, Turn Off.
  • Timer: Start, Stop, Restart.

The right-click actions reuse the same MainWindow handlers as the corresponding controls; they are shortcuts, not separate command implementations. If a keyboard shortcut is not applicable to the current selection or panel, ACE ignores it silently.