OP Stack L2 Follower Hardening — Config Recovery and Peer Pinning
OP Stack follower scripts should fail toward the real chain source, not toward empty placeholders. This chapter records the workshop-06 correction pattern: recover missing chain config from the sequencer source, pin the consensus peer deliberately, and keep sensitive coordinates out of public knowledge.
Why this became knowledge
During the 2026-06-20 road-to-dev review window, No.6 reported fixes to two workshop-06 submissions after P'Nut asked every Oracle to inspect its own PR. The useful part was not the status update itself; it was the implementation correction that appeared in the report.
The durable lesson: an OP Stack L2 follower should not create empty {} config files when required chain artifacts are missing. Empty placeholders can let the script continue into a misleading failure mode. A follower needs the real rollup.json and genesis.json from the sequencer source or it should stop clearly.
The corrected shape
- Detect missing config: before starting op-geth/op-node, check whether
rollup.jsonandgenesis.jsonexist and are non-empty. - Recover from the source: if a config file is missing, download it from the sequencer-owned file source instead of writing a dummy placeholder.
- Pin the consensus path: pass a deliberate
--p2p.static=...peer for op-node so the follower connects to the intended sequencer network path. - Apply the same rule everywhere: Docker Compose and shell-runner paths should share the same recovery and peer configuration behavior.
- Commit the fix on the submission branches: the report tied the correction to PR #5 and PR #18 in
workshop-06-arra-oracle-blockchain.
Do not preserve liveness by inventing config. Preserve correctness by recovering config from the authoritative source.
Redacted implementation sketch
# Before starting op-geth / op-node
for file in rollup.json genesis.json; do
if [ ! -s "$file" ]; then
curl -fsSL "$SEQUENCER_CONFIG_BASE/$file" -o "$file"
fi
done
# op-node consensus path
op-node \
--rollup.config=rollup.json \
--l1="$L1_RPC" \
--l2="$ENGINE_RPC" \
--p2p.static="$SEQUENCER_STATIC_PEER"
The Discord report contained a concrete file-server address and static peer multiaddr. They are intentionally not reproduced here. Store those values in environment variables, deployment config, or private workshop docs, not in a public-facing knowledge chapter.
Failure modes this prevents
| Failure mode | Bad behavior | Corrected behavior |
|---|---|---|
Missing rollup.json | Script creates {} and later fails far away from the cause. | Script downloads the real config or fails at the fetch step. |
Missing genesis.json | op-geth starts with invalid local assumptions. | Follower uses the sequencer-provided genesis artifact. |
| No deliberate peer | op-node may fail peer discovery or connect unpredictably. | Follower is pinned to the intended sequencer peer path. |
| Docker and shell drift | One runner works while the other still carries the old placeholder behavior. | Both runner surfaces enforce the same preflight and peer settings. |
Verification checklist
- Delete local
rollup.jsonandgenesis.jsonin a disposable worktree. - Run the shell script and confirm both files are restored from the configured source.
- Run the Docker Compose path and confirm it uses the same source and static peer value.
- Confirm op-node and op-geth advance to the same L2 head as the sequencer reference.
- Compare at least one historical block hash between local follower and reference chain.
This matches the prior proof pattern from the same workshop thread: local follower correctness is established by matching chain head and historical block hashes, not by merely starting containers.