-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.offline.yml
More file actions
105 lines (99 loc) · 6.02 KB
/
Copy pathdocker-compose.offline.yml
File metadata and controls
105 lines (99 loc) · 6.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# Sentinel — air-gapped offline stack (M11.4, ADR-030; standalone half of ADR-053).
#
# ZERO external calls at RUNTIME. The bundle is ASSEMBLED ONCE on a connected machine
# (scripts/build-airgap-bundle.sh — that step DOES pull images/models from the network), then
# physically transferred to the isolated host, where `docker load` + this file bring the stack up
# with no registry / DNS / internet access at any point.
#
# Air-gapped host bring-up (after transferring the bundle + `docker load` of every image tar):
# echo "OLLAMA_IMAGE_TAG=<tag from MANIFEST.sha256>" > .env # the exact tag docker save'd
# docker volume create sentinel_ollama-models # restore the model tar into it
# docker compose -f docker-compose.offline.yml --profile demo run --rm demo # zero-dep smoke
# docker compose -f docker-compose.offline.yml --profile ollama up -d ollama # local model endpoint
# docker compose -f docker-compose.offline.yml run --rm sentinel run --target <URL> # a real run
#
# Browsing the bundled docs offline (the files are baked into the image at /app/docs): a webui service
# is intentionally NOT in this stack — a container whose ONLY network is `internal: true` cannot publish
# host ports (Docker drops the mapping), so a webui here would be unreachable. Serve them with a single
# throwaway container on the default bridge instead (a passive file server that initiates no egress):
# docker run --rm -p 127.0.0.1:8088:8088 --entrypoint /app/.venv/bin/python \
# sentinel:local -m http.server 8088 --directory /app/docs # -> http://localhost:8088/
#
# Unlike docker-compose.yml, the offline base anchor carries NO `build:` key on purpose — a true
# air-gapped host has no base images to build FROM, so every service must resolve ONLY from a
# docker-load'ed image (`pull_policy: never` fails closed if an image is missing, never reaches out).
x-sentinel-offline-base: &sentinel-offline
image: sentinel:local # loaded via `docker load` from the bundle — NOT built here
pull_policy: never # never contact a registry; fail closed if the image is absent
volumes:
- ./runs:/app/runs # plan.json / transcript / heal-report / scenario.json / trace.zip
- ./state:/app/state # locator/golden/quarantine SQLite + store-gateway socket
- ./config:/config # mount a RunConfig YAML or plan.json (--run-config /config/run.yaml)
services:
# Primary agent. Reaches the bundled local model over the internal-only `airgap` network
# (ollama:11434 by service name — no host port needed). With the LLM_* block commented, runs fall
# back to the deterministic heuristic planner / L1–L6 heal — still fully offline. ENTRYPOINT is
# /app/bin/agentctl; append subcommands.
sentinel:
<<: *sentinel-offline
command: ["--help"]
networks: [airgap]
environment:
ANTHROPIC_API_KEY: "" # explicitly empty — no cloud path exists in an air-gapped deploy
# --- Local model via the bundled `ollama` service (uncomment to enable LLM heal) -----------
# LLM_BACKEND: openai
# LLM_BASE_URL: http://ollama:11434/v1
# LLM_MODEL: qwen2.5-vl:7b # the model preloaded into the bundle (docs/LOCAL_MODELS.md §3)
# LLM_API_KEY: noauth # Ollama ignores the key; the SDK requires a non-empty value
# LLM_VISION: "1" # qwen2.5-vl is vision-capable (set-of-marks heal re-grounding)
# Zero-external-dependency demo: deterministic explore against a bundled file:// fixture. Needs NO
# network at all, so it runs FULLY isolated (`network_mode: none`) — a strictly stronger guarantee
# than the internal airgap bridge. NOTE: `network_mode` cannot be combined with a `networks:` list,
# which is exactly why the shared anchor above declares no networks (each service opts in).
demo:
<<: *sentinel-offline
profiles: ["demo"]
network_mode: none
command:
- "run"
- "--target"
- "file:///app/testdata/site/index.html"
- "--planner"
- "heuristic"
- "--artifact-dir"
- "/app/runs/demo"
# Local, offline model endpoint (OpenAI-compatible at http://ollama:11434/v1, reachable by sentinel
# over the airgap network). The image is docker-load'ed from the bundle; OLLAMA_IMAGE_TAG pins the
# EXACT tag that build-airgap-bundle.sh saved (recorded in MANIFEST.sha256 + the generated .env —
# never a floating :latest at deploy). The model blobs are restored into the named volume
# `sentinel_ollama-models` (pinned via `name:` so it matches regardless of the compose project name);
# OLLAMA_NOPRUNE keeps hand-placed blobs. No host `ports:` — verification probes it via
# `docker compose exec` (a published port would not work on the internal network anyway).
ollama:
image: ollama/ollama:${OLLAMA_IMAGE_TAG:-latest}
pull_policy: never
profiles: ["ollama"]
environment:
OLLAMA_NOPRUNE: "1" # don't prune "orphan" blobs restored from the bundle tar
volumes:
- ollama-models:/root/.ollama
networks: [airgap]
healthcheck:
test: ["CMD", "ollama", "list"]
interval: 10s
timeout: 5s
retries: 12
networks:
# internal: true => Docker creates no gateway / NAT / masquerade rule for this bridge, so attached
# containers resolve each other by service name but CANNOT reach any external host. This is the
# structural enforcement of "zero external calls" (stronger than relying on --network none per run).
# Trade-off: an internal network also cannot publish host ports (verified) — hence no webui service
# and no ollama host port; inter-service comms use service names, host checks use `docker compose exec`.
airgap:
internal: true
volumes:
# `name:` pins the real Docker volume name so a bundle extracted into ANY directory (compose derives
# the project name from the dir basename) still mounts the SAME volume the bundle restores the model
# into — without this, `<dirname>_ollama-models` would be an empty, wrong volume (M11.4 review C1).
ollama-models:
name: sentinel_ollama-models