diff --git a/deploy/agent-host/check.sh b/deploy/agent-host/check.sh index 117c4a6..ac16a93 100755 --- a/deploy/agent-host/check.sh +++ b/deploy/agent-host/check.sh @@ -10,6 +10,11 @@ bad() { echo "FAIL: $1"; fail=1; } cd "$stack" || { echo "FAIL: no $stack"; exit 1; } [ "$(sudo docker compose ps --status running -q | wc -l)" -ge 1 ] \ && ok "SilverBullet is running" || bad "SilverBullet isn't running" +if sudo docker compose logs --no-color silverbullet 2>/dev/null | grep -q 'boot mode: single-space'; then + bad "it booted a single space with no accounts (an SB_* setting?)" +else + ok "it keeps accounts (no single-space boot)" +fi code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/) case $code in 2??|3??) ok "it answers locally ($code)" ;; *) bad "it answers $code" ;; esac probe=.nisse-probe-$$ diff --git a/deploy/agent-host/silverbullet/compose.yaml b/deploy/agent-host/silverbullet/compose.yaml index 73aaabc..862d666 100644 --- a/deploy/agent-host/silverbullet/compose.yaml +++ b/deploy/agent-host/silverbullet/compose.yaml @@ -8,9 +8,10 @@ services: image: ghcr.io/silverbulletmd/silverbullet:2.11.1-slim restart: unless-stopped environment: + # Only these. Any SB_* setting (even SB_NAME) boots a single space with + # no accounts, serving /data instead of the vault. PUID: "1000" PGID: "1000" - SB_NAME: Nisse vault ports: # IPv4 only; setup.sh's firewall lets only the operator's Caddy in. - "0.0.0.0:3000:3000" diff --git a/tests/deploy/test_agent_host.py b/tests/deploy/test_agent_host.py index 21f0b62..ec47048 100644 --- a/tests/deploy/test_agent_host.py +++ b/tests/deploy/test_agent_host.py @@ -67,6 +67,12 @@ def test_no_secrets_in_the_compose_file() -> None: assert not [k for k in environment if re.search(r"USER|TOKEN|PASS|SECRET|KEY", k)] +def test_it_boots_into_accounts_and_the_vault_not_a_bare_space() -> None: + # SB_NAME alone switched 2.11 to single-space mode: no accounts, no setup + # wizard, and /data served in place of the vault. + assert set(silverbullet()["environment"]) == {"PUID", "PGID"} + + def test_its_port_caddys_and_the_firewalls_agree() -> None: port = setting("SB_PORT") assert silverbullet()["ports"] == [f"0.0.0.0:{port}:3000"] # IPv4 only @@ -120,7 +126,9 @@ def test_the_firewall_lets_only_one_address_in_and_replaces_its_own_rule() -> No SCRIPT = HOST.parents[1] / "scripts" / "agent-host.sh" -def _fake_host(tmp_path: Path, *, memory_writable: bool) -> dict[str, str]: +def _fake_host( + tmp_path: Path, *, memory_writable: bool, boot: str = "multi-space" +) -> dict[str, str]: """Commands standing in for the host's: ssh runs the command here, sudo just runs it, and docker reads all of stdin, as docker compose exec does.""" bin_ = tmp_path / "bin" @@ -134,6 +142,7 @@ def _fake_host(tmp_path: Path, *, memory_writable: bool) -> dict[str, str]: "cat > /dev/null\n" 'case "$*" in\n' ' *"ps --status running"*) echo abc123 ;;\n' + f' *" logs "*) echo "boot mode: {boot}" ;;\n' f' *"/vault/memory/"*) exit {0 if memory_writable else 1} ;;\n' "esac" ), @@ -167,6 +176,7 @@ def test_the_check_runs_every_step_even_though_docker_reads_stdin( assert result.returncode == 0, result.stdout + result.stderr assert result.stdout.splitlines() == [ "ok: SilverBullet is running", + "ok: it keeps accounts (no single-space boot)", "ok: it answers locally (200)", "ok: the operator's folders are writable", "ok: memory/ is read-only to SilverBullet", @@ -184,3 +194,9 @@ def test_a_writable_memory_folder_fails_the_check(tmp_path: Path) -> None: ) assert "ok: the firewall rule is in place" in result.stdout # still checked assert "all checks passed" not in result.stdout + + +def test_a_single_space_boot_fails_the_check(tmp_path: Path) -> None: + result = _check(_fake_host(tmp_path, memory_writable=False, boot="single-space")) + assert result.returncode == 1 + assert "FAIL: it booted a single space with no accounts" in result.stdout