You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/OPERATIONS.md
+123Lines changed: 123 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -484,6 +484,129 @@ Add backup verification to your CI pipeline:
484
484
485
485
For detailed, step-by-step procedures to handle incident response, outages, containment, rollbacks, database restoration, and validation across the smart contract, backend, and frontend stacks, refer to the [Disaster Recovery Runbook](DISASTER_RECOVERY.md).
486
486
487
+
## Circuit-breaker manual-reset runbook
488
+
489
+
The `RiskManagementService` maintains an in-memory circuit breaker for every tracked asset (`XLM`, `BTC`, `ETH`, `USDC`, and any assets added via the admin API). A breaker **trips** when an asset's tick-over-tick price change exceeds **20 %** (the `CIRCUIT_BREAKER_THRESHOLD`). While tripped, `shouldAllowRebalance()` returns `allowed: false` with reason code `CIRCUIT_BREAKER_ACTIVE`, blocking all automatic and manual rebalance operations for any portfolio that holds the affected asset.
490
+
491
+
Tripped breakers auto-recover after **5 minutes** (`CIRCUIT_BREAKER_COOLDOWN`). The steps below are for situations where you need to reset before that window expires, or where you need to confirm the system is healthy after an incident.
`isTriggered: true` with a `cooldownUntil` value in the future confirms the breaker is active. Convert the Unix millisecond timestamp to determine how much cooldown remains:
This returns the per-asset breaker map, including `triggerReason` and the precise `cooldownUntil` timestamp.
539
+
540
+
---
541
+
542
+
### 2. Decide – reset manually or wait?
543
+
544
+
| Situation | Recommended action |
545
+
|-----------|-------------------|
546
+
| Cooldown expires in < 3 minutes |**Wait.** Auto-recovery will fire; no operator action needed. |
547
+
| Flash-crash or data anomaly confirmed as false alarm |**Reset manually.** Prices have stabilised and the trigger was a bad tick or feed glitch. |
548
+
| Market still highly volatile (> 15 % EWMA vol) |**Wait or investigate further.** Resetting into continued volatility will likely re-trip the breaker immediately. |
549
+
| Cooldown has expired but `isTriggered` is still `true` in status | Call `GET /api/system/status` again — `getCircuitBreakerStatus()` performs the expiry check lazily on each read. If the flag does not clear, restart the API process (see Safe shutdown and restart below). |
550
+
| Incident requires immediate production rebalancing | Follow the manual-reset steps below, then monitor `/api/risk/check/:portfolioId` continuously after the reset. |
551
+
552
+
---
553
+
554
+
### 3. Perform the manual reset
555
+
556
+
The admin endpoint accepts an `X-Admin-Key` header (value of the `ADMIN_API_KEY` environment variable) and optionally a specific asset to reset. Omitting `asset` resets **all** tripped breakers.
557
+
558
+
**Reset a single asset (e.g. BTC):**
559
+
560
+
```bash
561
+
curl -X POST https://<API_HOST>/api/admin/circuit-breaker/reset \
562
+
-H "Content-Type: application/json" \
563
+
-H "X-Admin-Key: <ADMIN_API_KEY>" \
564
+
-d '{"asset": "BTC"}'
565
+
```
566
+
567
+
**Reset all assets at once:**
568
+
569
+
```bash
570
+
curl -X POST https://<API_HOST>/api/admin/circuit-breaker/reset \
> **Note:** The `RiskManagementService` instance is in-process and in-memory. If the API runs as multiple instances behind a load balancer, send the reset request to **every instance** (or use a sticky session / internal broadcast mechanism). After any process restart the breaker state is cleared automatically.
589
+
590
+
---
591
+
592
+
### 4. Post-reset confirmation checklist
593
+
594
+
Run through the following checks after performing a reset to confirm the system has returned to normal operation:
595
+
596
+
-[ ]**Breaker cleared** – `GET /api/system/status` returns `alertsActive: false` and `isTriggered: false` for the affected asset(s).
597
+
-[ ]**Risk check passes** – `GET /api/risk/check/<PORTFOLIO_ID>` returns `"reasonCode": "CIRCUIT_BREAKER_ACTIVE"` no longer; `allowed: true` (assuming no other blocks are active).
598
+
-[ ]**Price feed is live** – `GET /api/system/status` shows `"priceFeeds": true` under `services`. Stale or absent prices will re-trip the breaker on the next price tick if volatility is still high.
599
+
-[ ]**Auto-rebalancer running** – `GET /api/system/status` → `autoRebalancer.status.isRunning: true`. If the auto-rebalancer paused due to the circuit-breaker event, restart it:
600
+
```bash
601
+
curl -X POST https://<API_HOST>/api/auto-rebalancer/start \
602
+
-H "X-Admin-Key: <ADMIN_API_KEY>"
603
+
```
604
+
-[ ]**No repeat trips** – Monitor `GET /api/system/status` for 5–10 minutes after the reset. If the breaker re-trips immediately, the underlying market condition has not stabilised; **do not keep resetting manually** — investigate the price feed or wait for conditions to calm.
605
+
-[ ]**Notification delivered** – If circuit-breaker notifications are enabled (`event_circuit_breaker` preference), confirm users received the event-cleared or rebalancing-resumed notification (check `GET /api/notifications` for recent entries).
606
+
-[ ]**Audit log entry** – Confirm the admin action is reflected in application logs (search for `circuit-breaker reset` at `INFO` level).
0 commit comments