Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,15 @@ bash .github/run-tests.sh # the whole offline suite

The runner is `.github/run-tests.sh`, invoked as `bash .github/run-tests.sh`
from the repo root. With no arguments its default member list is every
`tests/*.sh` in the repo — all nine today:
`tests/*.sh` in the repo — all ten today:

| Script | What it pins |
|---|---|
| `host_memory_test.sh` | `write_host_memory` — the writer of `$MINI_HOME/memories/00-host.md` |
| `init_test.sh` | init-system detection + doctor's Init step |
| `mini_bin_dir_test.sh` | the PATH the agent inherits, and the `/remember` command |
| `model_flag_test.sh` | the missing-argument refusal of `mini --model` |
| `ollama_bind_test.sh` | doctor's ollama bind verdict, read from the observed listener |
| `ollama_systemd_failure_test.sh` | systemd rebind and stop refusals — a failure is reported, not swallowed |
| `opencode_config_test.sh` | the `opencode.json` regeneration contract (skips without python3) |
| `remote_key_test.sh` | the API-key prompt of `mini remote` |
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,11 @@ Two things could listen, and mini locks down both:

- **ollama** is pinned to `127.0.0.1:11434`. An ollama on a public interface has
no authentication of any kind — anyone who finds it can run inference on your
hardware. `mini doctor` fails loudly if it sees a `0.0.0.0` bind, and
`mini serve --local` puts it back.
hardware. `mini doctor` reads the live listener and fails on any bind that
is not loopback — `0.0.0.0`, `::`, or a routable address such as
`192.168.1.10`. `mini serve --local` puts it back. "loopback only" is
claimed only after the listener has actually been inspected; without `ss`
doctor reports the bind as unverified.
- **opencode's server** (`mini web`, `mini headless`) binds to loopback and gets
a 32-character password generated on first use, stored `0600` in
`~/.cache/mini/secret`. `--lan` opens it to your network; the password is what
Expand Down
7 changes: 6 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,12 @@ the machine — unauthenticated, it would be a remote shell for anyone who can
reach the port.

ollama itself is pinned to `127.0.0.1:11434` and never exposed. `mini doctor`
fails loudly if it finds it bound to `0.0.0.0`; `mini serve --local` fixes it.
reads the live listener rather than the configured host: any bind that is not
loopback — `0.0.0.0`, `::`, or a routable address such as `192.168.1.10` — is
a failure, and `mini serve --local` fixes it. "bound to loopback only" is
reported only after the listener has actually been inspected; on a box without
`ss` the bind cannot be checked, so doctor calls it unverified and prints the
command that installs `ss`.

## Memory

Expand Down
35 changes: 30 additions & 5 deletions lib/doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,38 @@ doctor_run() {

# An ollama on a public interface has no authentication of any kind. Anyone
# who can reach it can load models and run inference on your hardware.
local public_bind
public_bind=$(ollama_public_bind)
if [ -n "$public_bind" ]; then
d_fail "ollama is listening on ${public_bind} — reachable from the network, with no auth"
#
# The verdict comes from the observed listener, not from OLLAMA_HOST: a
# server bound to a routable address (192.168.1.10:11434) is as reachable as
# one on 0.0.0.0, and the configured host says nothing about what is really
# listening. And when the listener cannot be inspected at all, doctor says
# so instead of claiming a bind it never saw.
#
# One snapshot, one verdict: the listener table is read exactly once, and
# both the public list and the loopback-only line come out of that read.
# Two reads would be two moments in time — the server can rebind between
# them, and doctor would call a routable bind safe because the first read
# happened to come back empty.
local binds='' addrs='' addr inspected=0
if binds=$(ollama_listen_binds); then inspected=1; fi
if [ "$inspected" -eq 1 ]; then
while IFS= read -r addr; do
[ -n "$addr" ] || continue
if ! ollama_addr_is_loopback "$addr"; then
if [ -z "$addrs" ]; then addrs="$addr"; else addrs="${addrs}, ${addr}"; fi
fi
done <<< "$binds"
fi
if [ -n "$addrs" ]; then
d_fail "ollama is listening on ${addrs} — reachable from the network, with no auth"
d_fix "mini serve --local (or set OLLAMA_HOST=127.0.0.1:11434 in the unit and restart)"
elif ollama_up && ollama_is_loopback; then
elif [ "$inspected" -eq 1 ] && [ -n "$binds" ]; then
# The listener was inspected, and every bind on the ollama port is loopback.
d_ok "bound to loopback only ${C_DIM}(not reachable from the network)${C_RESET}"
elif [ "$inspected" -eq 0 ]; then
# Nothing was inspected, so nothing may be claimed about the bind.
d_warn "could not inspect the ollama listener — the bind is unverified"
d_fix "$(platform_install_cmd_for ss)"
fi

step "Model"
Expand Down
44 changes: 38 additions & 6 deletions lib/ollama.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,47 @@ ollama_is_loopback() {
esac
}

# Is anything answering on the ollama port from outside this machine?
# Returns the offending bind address, or nothing.
ollama_public_bind() {
# Every address the ollama port is bound to, exactly as ss reports it:
# "127.0.0.1:11434", "0.0.0.0:11434", "[::]:11434", "192.168.1.10:11434".
# One address per line.
#
# Returns non-zero when the listener could not be inspected at all — no ss
# on PATH — so a caller can tell "the listener is on loopback" from "nobody
# looked". A missing ss must never read as a healthy bind.
ollama_listen_binds() {
local port="${OLLAMA_HOST_ADDR##*:}"
[ -n "$port" ] || port=11434
has ss || return 0
has ss || return 1
ss -tlnH 2>/dev/null \
| awk -v p=":${port}" '$4 ~ p"$" {print $4}' \
| grep -E '^(0\.0\.0\.0|\*|\[::\])' || true
| awk -v p=":${port}" '$4 ~ p"$" {print $4}'
}

# Is this bind address a loopback one? 127.0.0.0/8 and ::1 are.
ollama_addr_is_loopback() {
local host="$1"
case "$host" in
\[*\]*) host="${host#\[}"; host="${host%%\]*}" ;; # [::1]:11434 -> ::1
*) host="${host%:*}" ;; # 127.0.0.1:11434 -> 127.0.0.1
esac
case "$host" in
127.*|::1|localhost) return 0 ;;
*) return 1 ;;
esac
}

# Is anything answering on the ollama port from outside this machine?
# Returns the offending bind address, or nothing.
#
# Every non-loopback bind counts. 0.0.0.0 and [::] are wildcards, and a
# routable address (192.168.1.10:11434) is just as reachable from the
# network — matching the wildcards alone hid that one.
ollama_public_bind() {
local addr
while IFS= read -r addr; do
[ -n "$addr" ] || continue
ollama_addr_is_loopback "$addr" || printf '%s\n' "$addr"
done < <(ollama_listen_binds)
return 0
}
Comment thread
developerz-ai[bot] marked this conversation as resolved.
Resolved

# Is the server the one systemd owns?
Expand Down
3 changes: 3 additions & 0 deletions tests/init_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ CFG
ollama_up() { return 0; }
ollama_version() { printf '0.0.0\n'; }
ollama_public_bind() { return 0; }
# The listener helper too: the doctor run must never reach the host's
# real ss. A loopback bind is what a healthy host has.
ollama_listen_binds() { printf '127.0.0.1:11434\n'; }
ollama_is_loopback() { return 0; }
ollama_has_model() { return 0; }
models_recommend() { printf 'stub-model:1b\n'; }
Expand Down
Loading
Loading