Skip to content

Command Center

Bubori Attila edited this page Aug 11, 2026 · 2 revisions

Command Center

Batch, scheduled PowerShell/cmd command execution across many workstations at once - this is not the same as the Console feature described on the Client-Server Communication page. Console is immediate, single-machine, server-initiated push over port 8080; Command Center is scheduled, client-initiated pull, over the same port 443 API-Token channel used for heartbeat/event reporting.

Data model

Table Role
commands One row per created command: script text (command), earliest run time (run_after_at), Emergency Stop flag (blocked), creator (user_id)
command_workstations Pivot table: one row per (command × assigned workstation) pair, result field (NULL until a result comes back)

Creating a command

CommandsController::saveCommand() - requires the write-batch-command permission. Validates: a script is selected, ≥1 workstation is assigned, the date/time is in the future. Once at least one result has come back for a command (CommandWorkstations::whereNotNull("result")->count() > 0), the command can no longer be edited.

Execution flow - the client polls, the server does not push

sequenceDiagram
    participant C as BigLanService client
    participant S as BigLan server

    Note over C: On startup, then every 30 minutes
    C->>S: action: getWaitingCommandsCounter (wsid)
    S-->>C: number of pending commands

    C->>S: action: getCommand (wsid)
    Note over S: getCommand() immediately marks the row<br/>"[Retrieved]" so it isn't handed out twice
    S-->>C: command_workstations_id + script text

    Note over C: Script runs locally

    C->>S: action: saveCommandResult (id, result)
    Note over S: the "[Retrieved]" placeholder is<br/>overwritten with the real result
Loading
  • getWaitingCommandsCounter() - counts command_workstations rows where result is still NULL, joined to commands where blocked = 0 and run_after_at <= now
  • getCommand() - returns the oldest eligible pending command (ORDER BY run_after_at ASC) and immediately sets its result to "[Retrieved]" - this prevents the same command from being handed to the same client twice on overlapping polls
  • saveCommandResult() - the client calls this to send back the actual output, overwriting the placeholder

Emergency Stop

CommandsController::emergencyStop() sets commands.blocked = 1. From that point on, no new getWaitingCommandsCounter()/getCommand() call will hand out this command - but it does not recall it from a workstation that already retrieved it and is mid-execution.

Known limitation: the "[Retrieved]" placeholder never expires

If a client retrieves a command but never reports back a result (crash, network loss, etc.), the result field stays at "[Retrieved]" forever - there is no timeout or automatic re-issuing. This shows up as a permanently "stuck" entry in the Command Center list.

Clone this wiki locally