docs(runbook): reframe as one disease with two cures, intercept first
The previous commit called BE12599 a second failure mode and claimed the device "never enters S3 mode at all" and that no inbound work could reach it. That was an overclaim built on a single slow_drip attempt, and Brian was right to push back. It is the same disease. Method B's step 1 worked fine on BE12599 — clearing the Destination did stop the dial-outs. It was step 2 that did not land, on one attempt, run ~90 s after a modem reboot with a dead session visible in the log in that same window; BE9558H needed hours of attempts before one landed. And the AT-init loop the ALEOS log revealed is almost certainly what BE9558H was doing too — we just never turned on serial debug in May to look. The device speaks S3 fine; it handshook cleanly the moment it had a session. What is genuinely new is the cure, and it deserves to be the default rather than a footnote. Racing a Stop into the gaps between dial-outs is a coin flip. Intercepting is deterministic: the unit dials every ~75 s, so give it somewhere to dial and answer it. It will not answer us because it is on the phone — so be the one it calls. Restructures accordingly: a "two cures" table up top, the intercept promoted to Method A with its own procedure (listener before modem, stop at step 1.5, drain before disabling ACH, restore the Destination and confirm it), and the original inbound procedure kept intact as Method B for when there is no listener the modem can reach. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qcu9ByJfuKBQxmrWb8rSrN
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# Runbook — Recovering a wedged unit stuck in a call-home loop
|
# Runbook — Recovering a wedged unit stuck in a call-home loop
|
||||||
|
|
||||||
**Original incident:** BE9558H at `166.246.130.1:9034`, recovered 2026-05-17.
|
**Incidents:** BE9558H at `166.246.130.1:9034`, 2026-05-17 (Method B) ·
|
||||||
|
BE12599 at `166.246.64.226:9034`, 2026-09-16 (Method A).
|
||||||
|
|
||||||
A field unit with a stuck-triggered geophone (or any hardware fault causing
|
A field unit with a stuck-triggered geophone (or any hardware fault causing
|
||||||
constant event triggering) will record events back-to-back, and if Auto Call
|
constant event triggering) will record events back-to-back, and if Auto Call
|
||||||
@@ -14,24 +15,30 @@ This runbook describes how to break the loop and recover control.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚠ There are TWO failure modes under "wedged unit"
|
## ⚠ Two cures for one disease — intercept first
|
||||||
|
|
||||||
Same root cause — an offset/connector fault drives the geophone above trigger,
|
Both incidents below are the **same failure**: a geophone offset crosses the
|
||||||
the unit records back-to-back, ACH set to "after event recorded" dials
|
trigger level, the unit records back-to-back, ACH set to "after event
|
||||||
constantly — but the *recovery* differs, because the thing blocking you is
|
recorded" dials continuously, and the unit becomes unreachable because its
|
||||||
different.
|
modem is in client mode almost all of the time.
|
||||||
|
|
||||||
| | **BE9558H (2026-05)** | **BE12599 (2026-09)** |
|
There are two ways to get a Stop Monitoring command into it.
|
||||||
|
|
||||||
|
| | **A — intercept the call** (preferred) | **B — catch it between calls** (original) |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| What blocks you | Modem mode-flipping kills inbound TCP | Device never enters S3 mode at all |
|
| Idea | Be the server it dials. Point the modem's Destination at our own ACH server and answer it. | Clear the Destination so it stops dialing, then race a Stop into the gap. |
|
||||||
| Device state | Alive, in S3 mode, responsive once reached | Stuck repeating an **AT modem-init** string, deaf to S3 |
|
| Needs inbound? | **No — the unit calls us** | Yes: working inbound TCP to the modem |
|
||||||
| Fix direction | **Inbound** — clear Destination, slow-drip a Stop | **Inverted** — point Destination at our own ACH server and let the unit call *us* |
|
| Determinism | Deterministic — it dials every ~75 s, we only have to be listening | A race. BE9558H took ~7 h of attempts before one landed. |
|
||||||
| Winning tool | `scripts/slow_drip.sh` | `bridges/ach_server.py --stop-monitoring` |
|
| Tool | `bridges/ach_server.py --stop-monitoring` | `scripts/slow_drip.sh` |
|
||||||
|
| Proven on | BE12599, 2026-09-16 | BE9558H, 2026-05-17 |
|
||||||
|
|
||||||
**Tell them apart with the ALEOS serial log** (see "Turn on ALEOS_SERIAL debug"
|
**Method A is the standard procedure now.** The unit won't answer us because
|
||||||
below). If the device is emitting `ATQ1/ATE0/ATS0=2` every ~75 s, it is in the
|
it is on the phone — so stop dialing it and be the one it calls. It rings,
|
||||||
BE12599 mode and **no amount of inbound work will reach it** — skip to
|
we pick up, take its data, and tell it to stop calling here.
|
||||||
"Second incident" below.
|
|
||||||
|
Method B is kept because it is proven, and because A needs a listener the
|
||||||
|
modem can actually reach (public IP + forwarded port). When you have that,
|
||||||
|
don't race it — intercept it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -52,9 +59,85 @@ If you see *all* of these, the unit is in this exact failure mode.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick reference — how to recover
|
## Method A (preferred) — intercept the call
|
||||||
|
|
||||||
You need **ACEmanager access** to the unit's modem.
|
You need **ACEmanager access** and a host the modem can dial: public IP with
|
||||||
|
the listener's port forwarded to it.
|
||||||
|
|
||||||
|
### A1 — start the listener BEFORE touching the modem
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/serversdown/seismo-relay
|
||||||
|
tmux new -s rescue
|
||||||
|
.venv/bin/python -u bridges/ach_server.py --port 12345 \
|
||||||
|
-o bridges/captures/<unit>-diag --stop-monitoring -v
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠ **Listener first, always.** A Destination pointed at a dead port is the
|
||||||
|
worst state available — the device still dials, the modem still flips to
|
||||||
|
client mode, inbound stays blocked, and nothing gets delivered.
|
||||||
|
|
||||||
|
Do **not** add `--events-only` (it silently breaks dedup — see gotchas), and
|
||||||
|
do **not** add `--disable-ach` yet (see A4).
|
||||||
|
|
||||||
|
### A2 — point the modem at it
|
||||||
|
|
||||||
|
ACEmanager → **Serial → Port Configuration**:
|
||||||
|
|
||||||
|
| Field | Set to |
|
||||||
|
|---|---|
|
||||||
|
| **Destination Address** | the listener's public IP |
|
||||||
|
| **Destination Port** | the listener's port (e.g. `12345`) |
|
||||||
|
|
||||||
|
Apply. The modem auto-dials its Destination whenever serial data arrives
|
||||||
|
while the serial port is closed — so the unit's own retry cycle now lands on
|
||||||
|
you instead of nowhere.
|
||||||
|
|
||||||
|
### A3 — answer, and stop the bleeding
|
||||||
|
|
||||||
|
Within ~75 s you should see a call-in. `--stop-monitoring` fires SUB 0x97 at
|
||||||
|
step 1.5 — after the handshake, **before** the event walk — so the recording
|
||||||
|
halts at the earliest possible moment in the session. Confirm via
|
||||||
|
`rescue.json` in the session directory:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"peer": "166.246.64.226:60921", "stop_monitoring": "ok"}
|
||||||
|
```
|
||||||
|
|
||||||
|
That is the bleeding stopped. Everything after this is cleanup.
|
||||||
|
|
||||||
|
### A4 — drain the backlog, THEN disable ACH
|
||||||
|
|
||||||
|
⚠ **Order matters, and it is counter-intuitive.** Stopping monitoring also
|
||||||
|
removes your call-in trigger: ACH fires on "after event recorded", so with
|
||||||
|
recording stopped the unit has no reason to dial again. The backlog sitting
|
||||||
|
in its memory does **not** re-arm it.
|
||||||
|
|
||||||
|
So if the stored events are worth keeping — and on a fault unit they usually
|
||||||
|
are, they're the evidence — drain them across however many call-ins it takes
|
||||||
|
*before* you silence it. Only then add `--disable-ach` (or use
|
||||||
|
`scripts/rescue_device.sh <host> <port> --no-erase`).
|
||||||
|
|
||||||
|
If the unit has gone quiet and you still need it, cycling the modem produces
|
||||||
|
a call-in, and a unit with a scheduled daily call will dial at its configured
|
||||||
|
time regardless.
|
||||||
|
|
||||||
|
### A5 — restore the Destination, and confirm you did
|
||||||
|
|
||||||
|
Put `Destination Address` back to `0.0.0.0` (or the office Instantel ACH
|
||||||
|
server) once you are finished, and only stop the listener after that is done.
|
||||||
|
|
||||||
|
### A6 — do NOT re-enable ACH until the hardware fault is repaired
|
||||||
|
|
||||||
|
Otherwise the loop restarts the moment monitoring resumes and you run this
|
||||||
|
runbook again.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Method B (fallback) — catch it between calls
|
||||||
|
|
||||||
|
The original 2026-05 procedure. Use when you cannot stand up a listener the
|
||||||
|
modem can reach. You need **ACEmanager access** to the unit's modem.
|
||||||
|
|
||||||
### Step 1: stop the modem's mode-flipping
|
### Step 1: stop the modem's mode-flipping
|
||||||
|
|
||||||
@@ -287,9 +370,14 @@ recovery: ~7 hours of intermittent debugging across one evening.
|
|||||||
level. Constant triggering → constant recording → ACH "after event recorded"
|
level. Constant triggering → constant recording → ACH "after event recorded"
|
||||||
→ continuous dialing. Same disease as BE9558H.
|
→ continuous dialing. Same disease as BE9558H.
|
||||||
|
|
||||||
**But the recovery was the opposite direction**, and none of the Step 1–4
|
**Same disease, inverted cure.** Method B's Step 1 *did* work — clearing the
|
||||||
procedure above worked. Total time ≈ 5 h, of which ~90 min was spent on two
|
Destination stopped the dial-outs, confirmed in the ALEOS log. It was Step 2
|
||||||
red herrings documented below.
|
that didn't land, and rather than keep racing we turned the rescue around:
|
||||||
|
gave the unit a different server to call, and answered it.
|
||||||
|
|
||||||
|
Total time ≈ 5 h, of which ~90 min went to two red herrings documented below.
|
||||||
|
Much of the rest was rediscovering the May procedure, which is why the
|
||||||
|
"two cures" table now sits at the top of this file.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -302,7 +390,7 @@ DEBUG**, then view the serial log.
|
|||||||
It is the only thing that tells you what the *device* is actually saying.
|
It is the only thing that tells you what the *device* is actually saying.
|
||||||
Everything before we did this was guesswork.
|
Everything before we did this was guesswork.
|
||||||
|
|
||||||
## What the log showed — the device was never in S3 mode
|
## What the log showed — the unit is on the phone
|
||||||
|
|
||||||
Every ~75 seconds, verbatim:
|
Every ~75 seconds, verbatim:
|
||||||
|
|
||||||
@@ -326,9 +414,15 @@ Read that carefully:
|
|||||||
- The device therefore never receives `OK`, never progresses, and **retries
|
- The device therefore never receives `OK`, never progresses, and **retries
|
||||||
the identical 29 bytes forever**.
|
the identical 29 bytes forever**.
|
||||||
|
|
||||||
**Consequence: the device is not running the S3 protocol parser.** You can
|
**While it is in this state it is busy placing a call, not listening for
|
||||||
land a byte-perfect S3 frame on it and it will be ignored. This is why every
|
us.** This is almost certainly what BE9558H was doing too — we simply never
|
||||||
inbound approach failed, and it is the structural difference from BE9558H.
|
turned on ALEOS_SERIAL debug in May to look. It is not a different disease;
|
||||||
|
it is the same one, seen properly for the first time.
|
||||||
|
|
||||||
|
It is also the argument for Method A in one picture: the unit is mid-dial
|
||||||
|
every ~75 s, and our inbound Stop has to thread the gaps between those
|
||||||
|
attempts. Give it somewhere to dial and the problem inverts into a
|
||||||
|
deterministic one.
|
||||||
|
|
||||||
### Why `slow_drip` lied
|
### Why `slow_drip` lied
|
||||||
|
|
||||||
@@ -351,6 +445,12 @@ nobody was reading.
|
|||||||
⚠ **`send_error: null` + full duration is NOT success. Only
|
⚠ **`send_error: null` + full duration is NOT success. Only
|
||||||
`bytes_received > 0` is success.**
|
`bytes_received > 0` is success.**
|
||||||
|
|
||||||
|
⚠ **In fairness to slow_drip: it got exactly one attempt here**, run ~90 s
|
||||||
|
after a modem reboot, with a dead session visible in the log at 20:19:17 in
|
||||||
|
that same window. BE9558H took hours of attempts before one landed. Method B
|
||||||
|
was not ruled out on BE12599 so much as abandoned in favour of something that
|
||||||
|
doesn't need luck.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚠ Two red herrings that cost ~90 minutes
|
## ⚠ Two red herrings that cost ~90 minutes
|
||||||
|
|||||||
Reference in New Issue
Block a user