docs(broker): establish remote Docker deployment plan for nats-server with networking/security guides and add D-15~D-21 freshness guards

This commit is contained in:
2026-08-22 23:47:29 +09:00
parent c6b6c77ce4
commit 3523b9b1ea
7 changed files with 1215 additions and 65 deletions
+116 -7
View File
@@ -283,16 +283,11 @@ def test_d11_private_server_env_names_valid():
code_blocks = re.findall(r"```(?:bash|conf|yaml|)(.*?)```", content, re.DOTALL)
assert code_blocks, "No code blocks found in PRIVATE_SERVER.md"
# Known recognized MQTT env vars from broker_config_from_env()
recognized = {
"MQTT_BROKER", "MQTT_PORT", "MQTT_TLS", "MQTT_USERNAME", "MQTT_PASSWORD",
"MQTT_CLIENT_ID_PREFIX", "MQTT_CA_CERTS", "MQTT_CERTFILE", "MQTT_KEYFILE",
"MQTT_KEEPALIVE", "MAM_MQTT_HOST" # checked for exclusion
}
# Known recognized MQTT env vars from broker_config_from_env() and deployment
valid_mqtt_vars = {
"MQTT_BROKER", "MQTT_PORT", "MQTT_TLS", "MQTT_USERNAME", "MQTT_PASSWORD",
"MQTT_CLIENT_ID_PREFIX", "MQTT_CA_CERTS", "MQTT_CERTFILE", "MQTT_KEYFILE",
"MQTT_KEEPALIVE"
"MQTT_KEEPALIVE", "MQTT_BIND"
}
for block in code_blocks:
@@ -352,3 +347,117 @@ def test_d14_private_server_cli_args_valid():
)
assert "status --job " in code_blocks, "PRIVATE_SERVER.md must include cleanup step with status --job"
# --------------------------------------------------------------------------
# D-15 — (G-D5) store_dir in code blocks must be absolute path (/ or $HOME)
# and heredocs writing it must be unquoted (<<EOF).
# --------------------------------------------------------------------------
def test_d15_private_server_store_dir_valid():
doc_path = os.path.join(REPO_ROOT, "PRIVATE_SERVER.md")
with open(doc_path, "r", encoding="utf-8") as f:
content = f.read()
code_blocks = re.findall(r"```(?:bash|conf|yaml|)(.*?)```", content, re.DOTALL)
for i, block in enumerate(code_blocks):
for match in re.finditer(r'store_dir:\s*["\']?([^"\'\n]+)["\']?', block):
val = match.group(1).strip()
assert val.startswith("/") or val.startswith("$HOME"), (
f"Block #{i+1} store_dir '{val}' must start with '/' or '$HOME' (no literal ~)"
)
if "store_dir:" in block and "cat <<" in block:
assert "<<'EOF'" not in block, (
f"Block #{i+1} writes store_dir with quoted heredoc <<'EOF', which prevents $HOME expansion"
)
# --------------------------------------------------------------------------
# D-16 — (G-D6) nats image references in code fences must use pinned alpine
# --------------------------------------------------------------------------
def test_d16_private_server_nats_image_alpine_pinned():
doc_path = os.path.join(REPO_ROOT, "PRIVATE_SERVER.md")
with open(doc_path, "r", encoding="utf-8") as f:
content = f.read()
code_blocks = re.findall(r"```(?:bash|conf|yaml|)(.*?)```", content, re.DOTALL)
for i, block in enumerate(code_blocks):
nats_refs = re.findall(r'\bnats:([a-zA-Z0-9_.-]+)', block)
for tag in nats_refs:
assert tag != "latest", f"Block #{i+1} contains unpinned 'nats:latest'"
assert "-alpine" in tag or tag.startswith("2."), f"Block #{i+1} nats image '{tag}' must use alpine variant"
# --------------------------------------------------------------------------
# D-17 — (G-D7) Port 8222 in docker examples must be bound to 127.0.0.1
# --------------------------------------------------------------------------
def test_d17_private_server_monitoring_port_localhost_bound():
doc_path = os.path.join(REPO_ROOT, "PRIVATE_SERVER.md")
with open(doc_path, "r", encoding="utf-8") as f:
content = f.read()
code_blocks = re.findall(r"```(?:bash|conf|yaml|)(.*?)```", content, re.DOTALL)
for i, block in enumerate(code_blocks):
for match in re.finditer(r'["\']?([0-9a-zA-Z._$:-]*8222:8222)["\']?', block):
mapping = match.group(1).strip()
assert "127.0.0.1:8222:8222" in mapping, (
f"Block #{i+1} port 8222 must be bound to 127.0.0.1, got '{mapping}'"
)
# --------------------------------------------------------------------------
# D-18 — (G-D8) TLS examples must not use IP literals for MQTT_BROKER
# --------------------------------------------------------------------------
def test_d18_private_server_tls_examples_use_domain_names():
doc_path = os.path.join(REPO_ROOT, "PRIVATE_SERVER.md")
with open(doc_path, "r", encoding="utf-8") as f:
content = f.read()
code_blocks = re.findall(r"```(?:bash|conf|yaml|)(.*?)```", content, re.DOTALL)
for i, block in enumerate(code_blocks):
if "MQTT_TLS=1" in block or "port: 8883" in block:
for match in re.finditer(r'MQTT_BROKER=["\']?([0-9.]+)', block):
ip = match.group(1)
assert False, f"Block #{i+1} uses IP literal '{ip}' with TLS (must use DNS domain name for SAN verification)"
# --------------------------------------------------------------------------
# D-19 — (G-D9) Subject literals in config examples match DEFAULT_TOPIC_ROOT
# --------------------------------------------------------------------------
def test_d19_private_server_subject_literals_match_default_topic_root():
doc_path = os.path.join(REPO_ROOT, "PRIVATE_SERVER.md")
with open(doc_path, "r", encoding="utf-8") as f:
content = f.read()
sys.path.insert(0, os.path.join(REPO_ROOT, ".agents", "skills", "multi-agent-mux-delegate-job", "scripts"))
import mqtt_common
expected_prefix = mqtt_common.DEFAULT_TOPIC_ROOT.replace("/", ".")
matches = re.findall(r'["\'](python\.mqtt\.jobs\.[>*\w.]+)["\']', content)
assert matches, "Expected subject literals matching DEFAULT_TOPIC_ROOT in PRIVATE_SERVER.md"
for sub in matches:
assert sub.startswith(expected_prefix), f"Subject '{sub}' does not start with expected prefix '{expected_prefix}'"
# --------------------------------------------------------------------------
# D-20 — (G-R1) run_loop.sh exports MAM_ENV_FILE
# --------------------------------------------------------------------------
def test_d20_run_loop_exports_mam_env_file():
run_loop_path = os.path.join(REPO_ROOT, ".agents", "skills", "multi-agent-mux-loop", "scripts", "run_loop.sh")
with open(run_loop_path, "r", encoding="utf-8") as f:
content = f.read()
assert 'export MAM_ENV_FILE=' in content, "run_loop.sh must export MAM_ENV_FILE"
# --------------------------------------------------------------------------
# D-21 — (G-R2) MQTT_KEEPALIVE documented and no un-commented retry vars
# --------------------------------------------------------------------------
def test_d21_env_template_mqtt_var_coverage():
template_path = os.path.join(REPO_ROOT, ".mam.env.example")
with open(template_path, "r", encoding="utf-8") as f:
template = f.read()
assert "MQTT_KEEPALIVE" in template, ".mam.env.example must document MQTT_KEEPALIVE"
for line in template.splitlines():
line = line.strip()
if not line.startswith("#"):
assert "MQTT_RETRY_INTERVAL" not in line
assert "MQTT_MAX_RETRIES" not in line