AI Assistant

The dashboard's AI Assistant answers questions about your Netzilo deployment, diagnoses devices and proposes changes for you to approve. On a self-hosted server it runs on your own server, with an AI provider that the account owner chooses. This page covers the server side: what runs where, what it needs, how to add it to an older server, how to change its settings and how to fix it.

How it works

The Assistant has three parts:

PartWhere it runsWhat it does
ManagementThe management containerOwns the chat: checks who may use the Assistant, stores sessions, messages and the audit log, and carries out approved changes
WorkerThe support-worker containerThe agent. It runs one answer at a time and keeps nothing between answers. It has no inbound port: only management calls it, over the internal network, with a shared token
AI providerOutside the server, or inside your networkThe model. Chosen by the owner under Integrations > Artificial Intelligence, never configured on the worker

The worker holds no credential of its own. Every API call it makes uses the token of the person in the chat, so the Assistant can do exactly what that person can do. Every change it proposes runs only after that person approves it in the chat.

The AI provider here is the Assistant's own provider. It is not the same list as the providers of the AI Gateway under Edge > Providers.

Before you start

  • The server has the worker. Servers installed in September 2026 or later include it. Check in the compose folder (/opt/netzilo, or /opt/netzilo/run on a marketplace image):

    cd /opt/netzilo
    sudo grep -c 'support-worker' docker-compose.yml
    sudo docker compose ps support-worker
    

    0, or no such service, means an older server: see Add the worker to an older server. Otherwise the service must be healthy.

  • Outbound HTTPS from the server:

    DestinationNeeded forIf blocked
    The AI provider's endpointEvery answerThe Assistant cannot answer
    github.com and codeload.github.comRefreshing the runbooks the agent followsThe worker uses the copy built into its image

Step 1: Connect an AI provider

An account owner goes to Integrations > Artificial Intelligence and clicks Add Provider. Any vendor API or compatible endpoint works: OpenAI, Anthropic, or an OpenAI- or Anthropic-compatible gateway or model server in your own network. In the provider's model list, tick the models allowed for Assistant. Nothing is approved until you tick it.

The Artificial Intelligence tab of Integrations, with one Anthropic provider card used for Assistant, Log Analysis and Threat Analysis, and the Add Provider card

The endpoint must be reachable from the server, not only from your browser. See Artificial Intelligence for the provider dialog.

Administrators now see the Assistant button in the dashboard.

Step 2: Let regular users use it

Regular users get the Assistant on their Workplace page, for their own devices only. Go to Settings > Permissions, turn on Allow regular users to use the AI Assistant, and choose the Allowed groups. Only members of these groups can open it. See Permissions.

Change the worker's settings

The worker's settings are environment variables of the support-worker service in docker-compose.yml. Edit its environment: list, then recreate only that service:

cd /opt/netzilo
sudo docker compose up -d --no-deps support-worker
sudo docker compose ps support-worker

It reports healthy within about 30 seconds. Recreating it interrupts only an answer in progress.

VariableDefaultControls
SUPPORT_MAX_AGENT_STEPS12 on compose installsModel calls per answer. Raise to 16 if answers end with step limit reached; answering with what I have
SUPPORT_TURN_TIMEOUT_SEC900The worker's time limit for one answer
SUPPORT_THINKING_BUDGET_TOKENS4096Reasoning tokens per call on Anthropic models. 0 turns reasoning off
SUPPORT_OPENAI_REASONING_EFFORTmediumReasoning effort for OpenAI reasoning models: low, medium or high
SUPPORT_SKILLS_REFmainThe version of the runbooks to follow. Pin a tag or commit if they must not change without review
SUPPORT_SKILLS_REFRESH_MINUTES30How often runbooks are refreshed. 0 turns refreshing off and uses the copy in the image
HTTPS_PROXYnoneA corporate proxy for the provider and GitHub

SUPPORT_WORKER_TOKEN must stay equal to WorkerToken in the SupportConfig block of management.json. The worker refuses to start without it, and it refuses to start when a LANGCHAIN_ or LANGSMITH_ tracing variable is set, because tracing would send conversations to a third party.

Behind a corporate proxy. Set HTTPS_PROXY. If you also set HTTP_PROXY, add management to NO_PROXY, because the worker reaches management over plain HTTP on the internal network. If the proxy inspects TLS, mount its CA certificate into the container and point SSL_CERT_FILE at it.

Air-gapped servers. Set SUPPORT_SKILLS_REFRESH_MINUTES=0. The runbooks are then the ones in the image; a newer worker image brings newer ones. Connect an AI provider endpoint inside your network.

Upgrade the worker like any other component, see Upgrade and add components.

Add the worker to an older server

A server installed before September 2026 has no worker and no SupportConfig block. Never re-run the installer to add it: that erases the server.

Servers installed with the one-line installer. If the published installer has the component commands (check as described in Upgrade and add components), one command adds the worker with its token and settings, after a backup:

curl -fsSL https://pkg.netzilo.com/download/install-netzilo.sh | sudo bash -s -- install support-worker

AWS and Azure images, or an installer without the component commands. Add the worker in place. Take a backup first (see Backup and restore). The procedure keeps timestamped copies of both files it edits, generates a 40-character token, adds the support-worker service to docker-compose.yml and the SupportConfig block to management.json, starts the worker and restarts management. Management is down for the few seconds of its restart. It refuses to run twice. On marketplace images the worker image is downloaded from ghcr.io, which the default security rules allow.

if [ -f /opt/netzilo/run/docker-compose.yml ]; then C=/opt/netzilo/run; else C=/opt/netzilo; fi
cd "$C"
sudo grep -q '^  support-worker:' docker-compose.yml && echo "support-worker is already present; stop here"
stamp=$(date +%s)
sudo cp -a docker-compose.yml "docker-compose.yml.bak-$stamp"
sudo cp -a management.json "management.json.bak-$stamp"
TOKEN="$(openssl rand -base64 32 | tr -d '=+/' | head -c 40)"
sudo TOKEN="$TOKEN" python3 - <<'EOF'
import os, re
tok = os.environ["TOKEN"]
p = "docker-compose.yml"; text = open(p).read()
if re.search(r"^  support-worker:", text, re.M): raise SystemExit("support-worker already present")
mgmt = re.search(r"^  management:\n(?:(?!^  \S).*\n)*", text, re.M)
if not mgmt: raise SystemExit("no management service in docker-compose.yml")
extra = re.search(r"^    extra_hosts:\n(?:^      .*\n)+", mgmt.group(0), re.M)
block = (
    "  support-worker:\n"
    "    image: ghcr.io/netzilo/net-support-worker:latest\n"
    "    container_name: support-worker\n"
    "    restart: unless-stopped\n"
    "    networks:\n"
    "      - netzilo\n"
    + (extra.group(0) if extra else "")
    + "    environment:\n"
    f"      - SUPPORT_WORKER_TOKEN={tok}\n"
    "      - SUPPORT_MAX_AGENT_STEPS=16\n"
    "      - SUPPORT_TOOL_TIMEOUT_SEC=30\n"
)
top = re.search(r"^(volumes|networks):", text, re.M)
if not top: raise SystemExit("no top-level volumes:/networks: section in docker-compose.yml")
open(p, "w").write(text[:top.start()] + block + text[top.start():])
EOF
sudo TOKEN="$TOKEN" python3 - <<'EOF'
import json, os
p = "management.json"; cfg = json.load(open(p))
if cfg.get("SupportConfig", {}).get("WorkerURL"): raise SystemExit("SupportConfig already set")
cfg["SupportConfig"] = {
    "WorkerURL": "http://support-worker:8080",
    "WorkerToken": os.environ["TOKEN"],
    "ManagementURL": "http://management:80",
    "TurnTimeoutSec": 900,
}
with open(p, "w") as f:
    json.dump(cfg, f, indent=2); f.write("\n")
EOF
unset TOKEN
sudo docker compose config -q
sudo docker compose pull -q support-worker
sudo docker compose up -d support-worker
sudo docker compose restart management

management.json is edited in place on purpose: the file is mounted into the management container by path, and replacing it would leave the container reading the old copy.

Check that every service is Up and support-worker becomes healthy within about a minute (sudo docker compose ps). Then connect a provider as in Step 1 and ask the Assistant a first question. Delete the .bak- files once it works: they hold the same secrets as the live files.

To roll back the in-place procedure:

cd "$C"
sudo docker compose rm -sf support-worker
sudo cp -a "$(ls -t docker-compose.yml.bak-* | head -1)" docker-compose.yml
sudo cp -a "$(ls -t management.json.bak-* | head -1)" management.json
sudo docker compose restart management

Rotate the shared token

Both sides change together. An answer in progress at that moment fails. The commands work in either compose folder.

if [ -f /opt/netzilo/run/docker-compose.yml ]; then C=/opt/netzilo/run; else C=/opt/netzilo; fi
cd "$C"
sudo cp -a management.json "management.json.bak-$(date +%s)"
sudo cp -a docker-compose.yml "docker-compose.yml.bak-$(date +%s)"
NEW="$(openssl rand -base64 32 | tr -d '=+/' | head -c 40)"
sudo NEW="$NEW" python3 - <<'EOF'
import json, os, re
new = os.environ["NEW"]
cfg = json.load(open("management.json"))
cfg["SupportConfig"]["WorkerToken"] = new
json.dump(cfg, open("management.json", "w"), indent=2)
text = open("docker-compose.yml").read()
text, n = re.subn(r"(SUPPORT_WORKER_TOKEN=)\S+", lambda m: m.group(1) + new, text)
assert n == 1, "expected exactly one SUPPORT_WORKER_TOKEN line"
open("docker-compose.yml", "w").write(text)
EOF
unset NEW
sudo docker compose up -d --no-deps support-worker
sudo docker compose restart management

Audit and data

Every Assistant session creates a Support session created event in Activity > Events. Administrators can open the read-only log of any session from that event, even after its owner deleted it. Sessions, messages and the audit trail are stored in management's database, so the normal backup covers them. The worker stores nothing and does not log conversation text.

Troubleshooting

No Assistant button for anyone. No model is approved for Assistant. An owner approves one under Integrations > Artificial Intelligence.

Admins see the button, a regular user does not. Allow regular users to use the AI Assistant is off, or the user is in none of the Allowed groups.

support agent is not configured on this deployment. management.json has no SupportConfig block: the server predates the worker. See Add the worker to an older server.

support agent is unavailable, try again shortly. Management cannot reach the worker: it is down or still starting. Check sudo docker compose ps support-worker and its log.

support worker returned HTTP 401. The two copies of the token differ. Make SUPPORT_WORKER_TOKEN equal to WorkerToken, or rotate the token.

The worker keeps restarting with refusing to start: SUPPORT_WORKER_TOKEN is empty. The token is missing from the worker's environment.

The AI provider rejected the configured API key. The key is wrong, expired or revoked. The owner fixes the provider under Integrations > Artificial Intelligence.

The configured model was not found at the AI provider. The provider does not serve that model. Approve another one.

The AI provider could not be reached (network error or timeout). The server has no outbound access to the provider, the proxy is not set, or a TLS-inspecting proxy's CA is not trusted.

Answers end with step limit reached; answering with what I have. Raise SUPPORT_MAX_AGENT_STEPS to 16.

To see the worker's log:

cd /opt/netzilo
sudo docker compose logs --tail=200 support-worker