@@ -66,6 +66,44 @@ report_failure() {
6666 --property=SubState --property=Result --property=ExecMainStatus >&2
6767 sudo journalctl -u " $unit " --no-pager -n 12 >&2 || true
6868 fi
69+ if [[ " $unit " == " agentnet-c0-responder.service" ]] &&
70+ sudo test -f /var/lib/agentnet-c0/config.json &&
71+ sudo test -f /var/lib/agentnet/guided-join.key.pem &&
72+ [[ -x " ${PYTHON_0144:- } " ]]; then
73+ sudo install -o agentnet-c0 -g agentnet-c0 -m 0400 \
74+ /var/lib/agentnet/guided-join.key.pem " $WORK /c0-probe-key.pem"
75+ sudo -u agentnet-c0 env \
76+ HOME=/var/lib/agentnet-c0 \
77+ NO_PROXY=" $NO_PROXY_VALUE " no_proxy=" $NO_PROXY_VALUE " \
78+ " $PYTHON_0144 " - /var/lib/agentnet-c0/config.json " $WORK /c0-probe-key.pem" << 'PY ' >&2 || true
79+ import json
80+ import sys
81+ from pathlib import Path
82+
83+ from agentnet.supervisor.c0_responder import _client, load_c0_responder_config
84+
85+ client, _core = _client(
86+ load_c0_responder_config(Path(sys.argv[1])),
87+ Path(sys.argv[2]),
88+ transport=None,
89+ )
90+ try:
91+ response = client.c0_pilot_readiness()
92+ try:
93+ body = response.json()
94+ except ValueError:
95+ body = {"message": "non-JSON response"}
96+ safe_body = {
97+ key: body[key]
98+ for key in ("schema", "status", "code", "message")
99+ if isinstance(body, dict) and key in body
100+ }
101+ print(json.dumps({"c0_probe_status": response.status_code, "body": safe_body}, sort_keys=True))
102+ finally:
103+ client.close()
104+ PY
105+ sudo rm -f " $WORK /c0-probe-key.pem"
106+ fi
69107 done
70108 return " $status "
71109}
@@ -244,17 +282,72 @@ plan_setup() {
244282 server-agent setup --request " $INPUTS /server-setup.json"
245283}
246284
285+ RELEASED_SETUP_START_RECONCILED=false
286+
287+ released_setup_is_live () {
288+ local prefix=" $1 "
289+ local version
290+ version=" $( sudo jq -r ' .version' " $prefix /lib/node_modules/@misunders2d/agentnet/package.json" ) "
291+ local unit
292+ for unit in agentnet-core.service agentnet-approval.service agentnet-c0-responder.service; do
293+ [[ " $( sudo systemctl show " $unit " --property=ActiveState --value) " == " active" ]] || return 1
294+ [[ " $( sudo systemctl show " $unit " --property=UnitFileState --value) " == " enabled" ]] || return 1
295+ done
296+ [[ " $( sudo systemctl show agentnet-credential-renew.timer --property=ActiveState --value) " == " active" ]] || return 1
297+ [[ " $( sudo systemctl show agentnet-credential-renew.timer --property=UnitFileState --value) " == " enabled" ]] || return 1
298+ sudo jq -e --arg version " $version " \
299+ ' .package_version == $version and .profile == "always_on_server_agent"' \
300+ /var/lib/agentnet-setup/setup.json > /dev/null || return 1
301+ env NO_PROXY=" $NO_PROXY_VALUE " no_proxy=" $NO_PROXY_VALUE " \
302+ curl --fail --silent --show-error https://approval.agentnet.test/healthz |
303+ jq -e --arg version " $version " \
304+ ' .service == "agentnet-approval" and .version == $version and .status == "alive"' \
305+ > /dev/null || return 1
306+ env NO_PROXY=" $NO_PROXY_VALUE " no_proxy=" $NO_PROXY_VALUE " \
307+ curl --fail --silent --show-error https://core.agentnet.test/healthz |
308+ jq -e --arg version " $version " \
309+ ' .service == "agentnet-core" and .version == $version and .status == "alive"' \
310+ > /dev/null
311+ }
312+
313+ wait_for_released_setup_live () {
314+ local prefix=" $1 "
315+ local attempt
316+ for attempt in $( seq 1 120) ; do
317+ if released_setup_is_live " $prefix " ; then
318+ return 0
319+ fi
320+ sleep 0.25
321+ done
322+ return 1
323+ }
324+
247325apply_setup () {
248326 local prefix=" $1 "
249327 local digest=" $2 "
250328 local output=" $3 "
329+ local exit_code=0
251330 run_evidence " $output " sudo -- env \
252331 PATH=" $prefix /bin:/usr/bin:/bin" \
253332 AGENTNET_UV=" $prefix /bin/uv" \
254333 NO_PROXY=" $NO_PROXY_VALUE " no_proxy=" $NO_PROXY_VALUE " \
255334 " $prefix /bin/node" " $( launcher " $prefix " ) " \
256335 server-agent setup --request " $INPUTS /server-setup.json" \
257- --expected-request-digest " $digest " --apply --start
336+ --expected-request-digest " $digest " --apply --start || exit_code=$?
337+ if [[ " $exit_code " -eq 0 ]]; then
338+ return 0
339+ fi
340+ if [[ " $prefix " == " $PREFIX_0144 " ]] &&
341+ jq -e ' .status == "blocked" and .blocker == "systemd_start"' " $output " > /dev/null &&
342+ wait_for_released_setup_live " $prefix " ; then
343+ # Released 0.1.50 can lose the final systemctl response after the immutable
344+ # marker, exact units, and exact health identities have all converged. Do
345+ # not reapply while those units are live; retain the blocked response and
346+ # record only the independently observed postcondition.
347+ RELEASED_SETUP_START_RECONCILED=true
348+ return 0
349+ fi
350+ return " $exit_code "
258351}
259352
260353psql_agentnet () {
@@ -622,7 +715,11 @@ jq -e --arg harness "$HARNESS_ID" --arg credential "$CREDENTIAL_ID" \
622715sudo install -d -o root -g root -m 0755 /var/lib/systemd/timers
623716sudo touch /var/lib/systemd/timers/stamp-agentnet-credential-renew.timer
624717apply_setup " $PREFIX_0144 " " $DIGEST_0144 " " $APPLY_BOUND_0144 "
625- jq -e ' .status == "operational" and .identity_enrolled == true and .authority_granted == false' " $APPLY_BOUND_0144 " > /dev/null
718+ if [[ " $RELEASED_SETUP_START_RECONCILED " == " true" ]]; then
719+ jq -e ' .status == "blocked" and .blocker == "systemd_start"' " $APPLY_BOUND_0144 " > /dev/null
720+ else
721+ jq -e ' .status == "operational" and .identity_enrolled == true and .authority_granted == false' " $APPLY_BOUND_0144 " > /dev/null
722+ fi
626723sudo grep -Fxq ' OnUnitActiveSec=1h' /etc/systemd/system/agentnet-credential-renew.timer
627724sudo grep -Fxq ' Persistent=true' /etc/systemd/system/agentnet-credential-renew.timer
628725! sudo grep -Fq ' OnUnitInactiveSec=' /etc/systemd/system/agentnet-credential-renew.timer
0 commit comments