Reference

Technical Appendix

Commands only. What each step does, why it is built this way, and the judgment behind it are in the book.

What's here

Setting up WSL2 and Docker on Windows

Step 1, Open PowerShell as Administrator

PowerShell, confirm you are elevated
> $id = [Security.Principal.WindowsIdentity]::GetCurrent()
> $me = New-Object Security.Principal.WindowsPrincipal($id)
> $me.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
Expected
True

A simpler check

PowerShell
> whoami /groups | Select-String 'Mandatory Level'
Elevated
Mandatory Label\High Mandatory Level    Label   S-1-16-12288
Not elevated
Mandatory Label\Medium Mandatory Level  Label   S-1-16-8192

Step 2, Confirm virtualization is available

PowerShell
> $cs  = Get-CimInstance Win32_ComputerSystem
> $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
> [PSCustomObject]@{
    HypervisorPresent = $cs.HypervisorPresent
    FirmwareVirt      = $cpu.VirtualizationFirmwareEnabled
    SLAT              = $cpu.SecondLevelAddressTranslationExtensions
  } | Format-List
Result A, a hypervisor is already running (most common)
HypervisorPresent : True
FirmwareVirt      : False
SLAT              : False
Result B, no hypervisor yet, hardware ready
HypervisorPresent : False
FirmwareVirt      : True
SLAT              : True
Result C, needs fixing in firmware
HypervisorPresent : False
FirmwareVirt      : False
SLAT              : True

Step 2b, Check the Windows features

PowerShell, needs Administrator
> $id = [Security.Principal.WindowsIdentity]::GetCurrent()
> $me = New-Object Security.Principal.WindowsPrincipal($id)
> $admin = $me.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
> if (-not $admin) {
    Write-Warning 'Not elevated. Reopen as Administrator, then retry.'
  } else {
    'VirtualMachinePlatform','Microsoft-Windows-Subsystem-Linux' |
      ForEach-Object {
      Get-WindowsOptionalFeature -Online -FeatureName $_ |
        Select-Object FeatureName, State
    }
  }
Expected, Disabled is normal at this point
FeatureName                            State
-----------                            -----
VirtualMachinePlatform               Enabled
Microsoft-Windows-Subsystem-Linux   Disabled

Step 3, Install WSL2 and Ubuntu

PowerShell, needs Administrator
> wsl --install -d Ubuntu-24.04
> wsl --set-default-version 2

Step 4, Verify you're on version 2

PowerShell, needs Administrator
> wsl --update
> wsl --list --verbose
> wsl --version
Expected
  NAME            STATE           VERSION
* Ubuntu-24.04    Running         2

WSL version: 2.3.26.0
Kernel version: 5.15.167.4-1
Windows version: 10.0.26100.2605

Step 4b, Name the machine and the user

One file controls all of it

Ubuntu, /etc/wsl.conf
$ sudo tee /etc/wsl.conf > /dev/null <<'EOF'
[boot]
systemd = true

[network]
hostname = darkweb-lab

[user]
default = darkweb
EOF

$ cat /etc/wsl.conf
PowerShell, apply
> wsl --shutdown
# wait ~10 seconds, then reopen Ubuntu
Ubuntu, confirm
$ hostname
$ whoami
Expected
darkweb-lab
darkweb

Changing the user account

Ubuntu, Case A: add a new account
$ sudo adduser darkweb
$ sudo usermod -aG sudo darkweb

# then set default = darkweb in /etc/wsl.conf and shut down
PowerShell, Case B: enter as root to rename
> wsl --shutdown
> wsl -d Ubuntu-24.04 -u root
Ubuntu, as root: rename oldname to darkweb
# usermod -l darkweb -d /home/darkweb -m oldname
# groupmod -n darkweb oldname
# id darkweb

Step 5, Allocate resources

PowerShell, creates %UserProfile%\.wslconfig
> notepad "$env:USERPROFILE\.wslconfig"
.wslconfig, adjust to your machine
[wsl2]
memory=12GB
processors=4
swap=4GB
localhostForwarding=true

# Docker pulls fail without this — see the note below
kernelCommandLine = ipv6.disable=1
PowerShell, apply
> wsl --shutdown
# wait ~10 seconds, then reopen Ubuntu

Step 6, Install Docker

Option A, Docker Desktop
# 1. Download and install Docker Desktop for Windows:
#      https://www.docker.com/products/docker-desktop/
#    or, from this elevated PowerShell window:
> winget install --id Docker.DockerDesktop --accept-source-agreements

# 2. Settings → General → "Use the WSL 2 based engine"        [checked]
# 3. Settings → Resources → WSL Integration → Ubuntu-24.04    [enabled]
# 4. Apply & Restart, then verify from inside Ubuntu:
$ docker run --rm hello-world
Option B, Docker Engine, inside Ubuntu
$ sudo apt-get update
$ sudo apt-get install -y ca-certificates curl
$ sudo install -m 0755 -d /etc/apt/keyrings
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
       -o /etc/apt/keyrings/docker.asc
$ sudo chmod a+r /etc/apt/keyrings/docker.asc

$ . /etc/os-release
$ ARCH=$(dpkg --print-architecture)
$ KEY=/etc/apt/keyrings/docker.asc
$ REPO=https://download.docker.com/linux/ubuntu

$ echo "deb [arch=$ARCH signed-by=$KEY] $REPO $VERSION_CODENAME stable" \
    | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

$ sudo apt-get update
$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
       docker-buildx-plugin docker-compose-plugin

# run docker without sudo — log out and back in afterwards
$ sudo usermod -aG docker $USER
Ubuntu, Option B only: confirm systemd
# you set this in Step 4b — verify rather than rewrite
$ grep -A1 '\[boot\]' /etc/wsl.conf
$ systemctl is-active docker

Step 7, Where the repository lives

Ubuntu
# correct — the Linux filesystem
$ cd ~ && pwd
/home/darkweb

# wrong — this is your Windows drive, mounted over a network protocol
#   /mnt/c/Users/you/darkweblabs
Ubuntu, measure the difference yourself
# one helper, so both tests are identical
$ bench() { time (mkdir -p "$1" && for i in $(seq 1 2000);
    do echo x > "$1/$i"; done); }

$ echo "--- Linux home ---"      ; bench ~/fstest
$ echo "--- Windows /mnt/c ---"  ; bench /mnt/c/fstest

$ rm -rf ~/fstest /mnt/c/fstest
Example run; your numbers will differ
--- Linux home ---
real    0m0.098s
user    0m0.011s
sys     0m0.073s

--- Windows /mnt/c ---
real    0m4.275s
user    0m0.031s
sys     0m0.353s

A, Moving the lab to another drive

PowerShell, check your free space
> Get-PSDrive -PSProvider FileSystem |
    Select-Object Name,
      @{n='FreeGB';e={[math]::Round($_.Free/1GB,1)}},
      @{n='UsedGB';e={[math]::Round($_.Used/1GB,1)}}
Example
Name FreeGB UsedGB
---- ------ ------
C      42.3  431.2
D     856.7  102.4

Method 1, move it in place

PowerShell, needs Administrator
> wsl --shutdown
> mkdir D:\wsl -Force
> wsl --manage Ubuntu-24.04 --move D:\wsl\Ubuntu-24.04
Expected
The operation completed successfully.
PowerShell, find out what's holding it
> wsl --shutdown
> wsl --list --running
What you need to see
There are no running distributions.

Method 2, export and re-import

PowerShell, needs Administrator
> wsl --shutdown
> mkdir D:\wsl -Force

# 1. export the whole distribution to a tar archive
> wsl --export Ubuntu-24.04 D:\wsl\ubuntu-backup.tar

# 2. remove it from C: — the tar is your only copy at this point
> wsl --unregister Ubuntu-24.04

# 3. re-import it onto the new drive
> wsl --import Ubuntu-24.04 D:\wsl\Ubuntu-24.04 `
      D:\wsl\ubuntu-backup.tar --version 2

After Method 2, check your user

PowerShell, apply and confirm
> wsl --shutdown
> wsl -d Ubuntu-24.04 whoami
Expected; your username, not root
yourusername

Docker's storage

Verify the move worked

PowerShell
> wsl --list --verbose
> Get-ChildItem D:\wsl -Recurse -Filter *.vhdx |
    Select-Object FullName,
      @{n='SizeGB';e={[math]::Round($_.Length/1GB,2)}}
Expected
  NAME            STATE           VERSION
* Ubuntu-24.04    Stopped         2

FullName                            SizeGB
--------                            ------
D:\wsl\Ubuntu-24.04\ext4.vhdx         2.14

Exclude the lab from real-time scanning

PowerShell, needs Administrator
> Add-MpPreference -ExclusionPath 'D:\wsl'
> Add-MpPreference -ExclusionProcess 'wsl.exe'
> Add-MpPreference -ExclusionProcess 'wslservice.exe'

> Get-MpPreference | Select-Object -ExpandProperty ExclusionPath
PowerShell, after rebooting
> wsl -d Ubuntu-24.04 -- hostname
> wsl --list --verbose
Expected
darkweb-lab

  NAME            STATE           VERSION
* Ubuntu-24.04    Running         2

Running the lab from a USB drive

PowerShell, move to the external drive
> wsl --shutdown
> wsl --list --running
> mkdir E:\wsl -Force
> wsl --manage Ubuntu-24.04 --move E:\wsl\Ubuntu-24.04

Using the drive on a second machine

PowerShell, on the second machine
> wsl --import-in-place Ubuntu-24.04 `
      E:\wsl\Ubuntu-24.04\ext4.vhdx

> wsl -d Ubuntu-24.04

B, Git and GitHub configuration

Ubuntu, identity and defaults
$ git config --global user.name  "Your Name"
$ git config --global user.email "you@example.com"
$ git config --global init.defaultBranch main
$ git config --global core.autocrlf input
$ git config --global pull.rebase false

# confirm
$ git config --global --list

Case A, HTTPS with a personal access token

Ubuntu, cache the token so you type it once
# Option 1 — hold it in memory for an hour at a time
$ git config --global credential.helper 'cache --timeout=3600'

# Option 2 — reuse Windows Credential Manager from inside WSL (recommended)
$ git config --global credential.helper \
    "/mnt/c/Program Files/Git/mingw64/bin/git-credential-manager.exe"
Ubuntu, first push prompts for credentials
# Username: your GitHub username
# Password: paste the token — NOT your account password

Case B, SSH key

Ubuntu, generate the key
$ ssh-keygen -t ed25519 -C "darkweb-lab $(hostname)"

# accept the default path: ~/.ssh/id_ed25519
# set a passphrase — leaving it empty means anyone with the file has the key

$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/id_ed25519
$ chmod 644 ~/.ssh/id_ed25519.pub
Ubuntu, load the key into the agent
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_ed25519
$ ssh-add -l
Ubuntu, start the agent automatically each session
$ cat >> ~/.bashrc <<'EOF'

# start ssh-agent once per WSL session and reuse it
if [ -z "$SSH_AUTH_SOCK" ]; then
  eval "$(ssh-agent -s)" > /dev/null
  ssh-add ~/.ssh/id_ed25519 2> /dev/null
fi
EOF

$ source ~/.bashrc
Ubuntu, copy the public key
$ cat ~/.ssh/id_ed25519.pub
Expected, one line, starts with ssh-ed25519
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... darkweb-lab DESKTOP-4K2L9
Ubuntu, test the connection
$ ssh -T git@github.com
Expected
Hi yourusername! You've successfully authenticated, but GitHub does not
provide shell access.

If your network blocks SSH

Ubuntu, ~/.ssh/config
$ cat >> ~/.ssh/config <<'EOF'
Host github.com
  Hostname ssh.github.com
  Port 443
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes
EOF

$ chmod 600 ~/.ssh/config
$ ssh -T git@github.com

Cloning, pushing, and switching remotes

Ubuntu, clone (choose one)
$ cd ~

# Case A — HTTPS
$ git clone https://github.com/toniall/darkweblabs.git

# Case B — SSH
$ git clone git@github.com:toniall/darkweblabs.git

$ cd darkweblabs
$ git remote -v
Ubuntu, starting a new repository from local files
$ cd ~/my-new-project
$ git init
$ git add .
$ git commit -m "Initial commit"
$ git branch -M main
$ git remote add origin git@github.com:toniall/my-new-project.git
$ git push -u origin main
Ubuntu, switch an existing clone between HTTPS and SSH
# HTTPS → SSH
$ git remote set-url origin git@github.com:toniall/darkweblabs.git

# SSH → HTTPS
$ git remote set-url origin https://github.com/toniall/darkweblabs.git

$ git remote -v

C, Running this site locally

Put it in place

Ubuntu
$ cd ~
$ git clone https://github.com/toniall/darkweblabs.git
# unpack the site package that came with the book into the clone
$ unzip ~/Downloads/darkweb-public-site.zip -d darkweblabs/site
$ cd darkweblabs/site && ls
Expected
assets  chapters  index.html  labs.html  LICENSE

Serve it, recommended

Ubuntu
$ python3 -m http.server 8080
Expected
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
Ubuntu, an alias, so you don't retype it
$ echo "alias site='python3 -m http.server 8080 \
    --directory ~/darkweblabs/site'" >> ~/.bashrc
$ source ~/.bashrc
$ site

Or open the file directly

Ubuntu
$ explorer.exe .
# then double-click index.html (or labs.html to go straight to the labs)

D, Troubleshooting

Jump to

Windows and PowerShell

Confirming an elevated terminal

Checking the Windows features

WSL version and hostname

Ubuntu, only if sudo complains
$ sudo sed -i "1i 127.0.1.1 $(hostname)" /etc/hosts
$ head -3 /etc/hosts

Editing .wslconfig

Docker can't pull images

Ubuntu, fix 1, then retry
$ ./lab fix-ipv6
# follow the printed steps, then reopen Ubuntu
$ ./lab up base
Ubuntu, fix 2, if it still fails
$ sudo systemctl restart docker
$ ./lab pin-registry
$ ./lab up base

# once the base images are downloaded, undo it:
$ ./lab unpin-registry
Ubuntu, on a machine with working network
$ BASES="debian:bookworm-slim nginx:1.27-alpine \
    kasmweb/core-ubuntu-jammy:1.16.0"
$ docker pull $BASES
$ docker save $BASES -o darkweb-bases.tar
Ubuntu, on the lab machine
$ ./lab load-images darkweb-bases.tar
$ ./lab doctor     # base images  all present

Why the obvious fixes don't work

PowerShell, open the file
> notepad "$env:USERPROFILE\.wslconfig"
.wslconfig, add under [wsl2]
kernelCommandLine = ipv6.disable=1
PowerShell, apply
> wsl --shutdown
# wait ten seconds, then reopen Ubuntu
Ubuntu, confirm
$ ip -6 addr
$ ./lab doctor
Expected
# ip -6 addr prints nothing at all

  ipv6 stack       absent — Docker will use IPv4 only

Image builds fail

Ubuntu
# use the default noble base
$ unset LAB_WS_BASE
$ ./lab up base

# or name a base explicitly
$ LAB_WS_BASE=kasmweb/core-ubuntu-noble:1.16.0 ./lab up base
Ubuntu
$ grep BASE_IMAGE compose.yml
$ ./lab selftest    # checks this specifically
Ubuntu
$ curl -s https://dist.torproject.org/torbrowser/16.0a9/ \
    | grep -o 'tor-browser-linux-[a-z0-9_]*-[^"]*\.tar\.xz' | sort -u

# then build with a version that has your architecture
$ LAB_TB_VERSION=16.0a9 ./lab up base

A container never becomes healthy

Ubuntu, see why
$ ./lab logs tor

# the container name works too
$ ./lab logs darkweb-tor
What to look for
Bootstrapped 0% (starting)
Bootstrapped 5% (conn)
...
Bootstrapped 100% (done)          <- this is what health waits for

The desktop rejects your credentials (401)

Ubuntu, ask the container, don't guess
$ ./lab creds

# or read it directly
$ docker exec darkweb-workstation \
    sh -c 'cut -d: -f1 "$HOME/.kasmpasswd"'

Changes appear to do nothing

Ubuntu, 1. is the repo internally consistent?
$ git pull
$ ./lab selftest
Ubuntu, 2. is Docker serving a cached layer?
$ ./lab rebuild
Ubuntu, 3. what did the branding step actually do?
$ ./lab rebuild 2>&1 | grep '\[branding\]'
What a working run looks like
[branding] wallpaper source: /tmp/branding/background.png
[branding] found xfce config: /home/.../xfce4-desktop.xml
[branding]   it points at: /usr/share/backgrounds/...
[branding]   wrote /usr/share/backgrounds/...
[branding] logo files replaced: 2
[branding] stylesheets patched: 3
[branding] files with vendor URLs rewritten: 1 -> https://abrandao.net
[branding] summary: wallpaper=3 logo=2 urls=1
Ubuntu, rotate the desktop password
$ ./lab rotate-creds
$ ./lab creds

The lab script won't run

Ubuntu
$ git pull
$ chmod +x lab labs/checks/*.sh
$ ./lab doctor
Ubuntu
$ sed -i 's/\r$//' lab labs/checks/*.sh
$ git config --global core.autocrlf input

An overlay testnet won't converge (I2P or Hyphanet)

Ubuntu
# each overlay is its own profile — they do not come up with the base lab
$ ./lab up i2p          # or: ./lab up hyphanet
$ ./lab ps              # every stack, base + overlays, in one view

# convergence signals
$ ./lab i2p netdb ff1   # I2P: routerInfo count should climb past zero
$ ./lab fn peers n1     # Hyphanet: darknet peers should reach "connected"

# the real gate is the on-host check
$ ./lab check 6.1       # or: ./lab check 7.1
Ubuntu
# pause an overlay — containers kept; ./lab up resumes it in seconds
$ ./lab stop i2p            # or: ./lab stop hyphanet | overlays

# delete one overlay's containers (keeps its seed volume)
$ ./lab down i2p            # or: ./lab down hyphanet | overlays

# start it over from a clean bootstrap (also drops the seed volume + network)
$ ./lab down hyphanet --volumes
$ ./lab up hyphanet

The range won't come up, or onions won't publish ( Chapter 8 )

Ubuntu
# bring the range up (generates watermarked content, then publishes onions)
$ ./lab up range
$ ./lab range list          # the published services + their (ephemeral) onions

# the answer key and the scorer work even before the services are reachable
$ ./lab range truth
$ ./lab range score sample-crawl.json

# the real gate is the on-host check
$ ./lab check 8.1           # confirms publication + watermarking

The crawler won't fetch, or won't score ( Chapter 9 )

Ubuntu
# the engine's brain, offline — the fastest way to prove the crawler itself is fine
$ ./lab crawl selftest

# a live crawl needs the range up; scope + seed come from ./lab range list
$ ./lab up range
$ ./lab crawl range            # the full engine; writes crawl-output.json to the evidence volume
$ ./lab crawl range --naive    # a first crawl with sessions, dedup, clone-detection off
$ ./lab crawl score            # crawl, then grade on the Lab 8.7 harness

The dedup detector's numbers look wrong, or ./lab dedup won't run ( Chapter 10 )

Ubuntu
# the whole detector, offline — the fastest way to prove it is fine
$ ./lab dedup selftest

# cluster the clone-lab corpus into mirrors and clones, and print the result
$ ./lab dedup run
$ ./lab dedup run --naive      # the Chapter 9 detector only (exact-hash + keyed-structural)

# grade the clustering against the shipped ground truth
$ ./lab dedup score            # full engine: mirror/clone recall 1.00, no false merges
$ ./lab dedup score --naive    # baseline: mirror recall 0.33, clone recall 0.50

The market extractor's numbers look wrong, or ./lab market won't run ( Chapter 11 )

Ubuntu
# the whole extractor, offline — the fastest way to prove it is fine
$ ./lab market selftest

# ingest the corpus into the content-addressed store (mirror collapses at storage)
$ ./lab market store

# parse listings/vendors and flag the market's lies; --naive is the brittle scraper
$ ./lab market extract
$ ./lab market graph          # resale rings, borrowed keys, gamed reputation, bait prices

# grade extraction against the shipped ground truth
$ ./lab market score          # full: field recall 1.00, all flags, poison refused
$ ./lab market score --naive  # baseline: 0.92, no flags, poison extracted

The leak/negotiation extractor's numbers look wrong, or ./lab leak won't run (Chapter 12)

Ubuntu
# the whole extractor, offline — the fastest way to prove it is fine
$ ./lab leak selftest

# reuse the Chapter 11 store on the leak corpus (unchanged victim collapses across snapshots)
$ ./lab leak store

# recover both surfaces; --naive is the brittle reader that believes the leak site
$ ./lab leak extract
$ ./lab leak lifecycle        # slid deadlines (theatre), quiet withdrawals
$ ./lab leak reposts          # mirror = affiliate movement, clone = recycled claim
$ ./lab leak negotiate        # the private arc + operator tactics
$ ./lab leak correlate        # the bluff is in the gap: deadline, volume, sold, deletion

# grade both surfaces against the shipped ground truth
$ ./lab leak score            # full: field 1.00, lifecycle/repost/tactic/bluff recall 1.00
$ ./lab leak score --naive    # baseline: 0.88 fields, 0.00 on everything two-surface

RansomChat won't start, the model won't load, or the chat is unreachable (Chapter 12, Lab 12.8 )

A real-data lab (11.8, 12.9, 14.8) won't run or shows nothing (Chapters 11, 12, 14)

./lab report produced nothing, or a page is blank in the workstation (Chapter 15)

The persona linker over-links or under-links, or ./lab link won't run (Chapter 13)

Ubuntu
# the whole linkage engine, offline — the fastest way to prove it is fine
$ ./lab link selftest

# the identifier ledger: what each persona signs vs merely shows
$ ./lab link ledger
$ ./lab link hard             # shared signed key/wallet links; displayed-only key = framing flag

# the soft signals — corroborate, never prove
$ ./lab link style            # writing voice; threshold set from the measured gap
$ ./lab link behavior         # rhythm (a timezone hint), handle transform, tactic sequence

# fuse into operators with confidence; --naive is the merge-on-any-string baseline
$ ./lab link fuse
$ ./lab link fuse --naive     # over-merges the frame + look-alike, splits the rotated operator

# grade against the shipped ground truth
$ ./lab link score            # full: recall 7/7, precision 7/7, false merges 0
$ ./lab link score --naive    # baseline: precision 0.40, nine false merges

./lab detect floods with alerts, buries the criticals, or won't run (Chapter 14)

Ubuntu
# the whole detection engine, offline — the fastest way to prove it is fine
$ ./lab detect selftest

# what the monitor watches — built from Chapter 13's high-confidence operators
$ ./lab detect watchlist

# the raw change feed, then each change typed into the taxonomy
$ ./lab detect feed             # 22 raw events — mostly churn and mirror duplicates
$ ./lab detect classify         # new_victim, publication, new_clone, operator_resurface, cosmetic_churn…

# score by severity (watched operators boosted), then collapse duplicates
$ ./lab detect score            # criticals to the top; churn to suppress
$ ./lab detect correlate        # a mirrored victim is one alert, not two

# the full watch loop, then the naive baseline that floods
$ ./lab detect monitor          # 8 ranked alerts, both criticals on top
$ ./lab detect monitor --naive  # 22 flat alerts, criticals buried mid-stream

# grade against the shipped ground truth
$ ./lab detect grade            # full: recall 8/8, precision 1.00, 0 false alerts, 2/2 criticals
$ ./lab detect grade --naive    # baseline: precision 0.36, 12 false alerts, 0/2 criticals

./lab capstone won't run, overclaims, or the report looks thin (Chapter 15)

Ubuntu
# the whole capstone, offline — runs the Ch11–14 engines and grades the report
$ ./lab capstone selftest

# chain the four engines into one evidence graph for the target operator
$ ./lab capstone evidence        # keyed by the reused signed key F19B7A0C…
$ ./lab capstone claims          # each claim: provenance + type + a calibrated confidence

# assemble the brief, then the careless baseline that overclaims
$ ./lab capstone report          # BLUF, findings by confidence, what-would-change, boundary
$ ./lab capstone report --naive  # flattens every finding to sourceless high-confidence fact

# grade against the ground-truth claim set
$ ./lab capstone grade           # full: coverage 8/8, provenance 1.00, calibration 1.00, 0 overclaims
$ ./lab capstone grade --naive   # baseline: same coverage 8/8, provenance 0.00, calibration 0.25, 6 overclaims

E, Running the labs on a VPS

What to skip

VPS, Docker Engine
$ sudo apt-get update
$ sudo apt-get install -y ca-certificates curl
$ sudo install -m 0755 -d /etc/apt/keyrings
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
       -o /etc/apt/keyrings/docker.asc
$ sudo chmod a+r /etc/apt/keyrings/docker.asc

$ . /etc/os-release
$ ARCH=$(dpkg --print-architecture)
$ echo "deb [arch=$ARCH signed-by=/etc/apt/keyrings/docker.asc] \
    https://download.docker.com/linux/ubuntu $VERSION_CODENAME stable" \
    | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

$ sudo apt-get update
$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
       docker-buildx-plugin docker-compose-plugin
$ sudo usermod -aG docker $USER
# log out and back in, then continue with Lab 1.1

Reaching the desktop

your own machine, not the VPS
$ ssh -L 6901:127.0.0.1:6901 you@your-vps

# leave that open, then browse locally to:
#   https://127.0.0.1:6901

What gets better, and what to watch

F, Clipboard, and why the lab prefers Chrome

What works where

Why Chrome, specifically

Ubuntu
$ LAB_CLIPBOARD_AUTO=0 ./lab rebuild

If it stops working

Ubuntu, the session side
$ docker exec darkweb-workstation pgrep -a vncconfig
$ docker exec darkweb-workstation pgrep -a autocutsel
Ubuntu, what the desktop holds
$ docker exec -e DISPLAY=:1 darkweb-workstation xclip -o -selection clipboard