Sentinel
Sentinel gives every incoming transfer an anomaly score from 0 (normal) to 1 (highly unusual). Depending on settings, the node then accepts it, flags it, refuses it, or — with the policy layer on — holds it for a while or until the owner confirms.
The model
A small network embedded in the node (8 inputs → 16 → 8 → 1, 289 parameters). Its inputs describe the transfer, not the person:
| Feature | From |
|---|---|
| Size of the amount (log scale) | value |
| Gas price | gas_price |
| Nonce gap | nonce vs the last confirmed nonce |
| Recipient is new | Prior transfers to the recipient |
| Round amount | value |
| Sender velocity | Transfers by the sender in the last 10 blocks |
| Share of balance moved | value vs balance |
| Self-transfer | from == to |
In consensus the node computes these from committed blocks only (velocity is counted from blocks, not from the mempool), so every validator gets the same score for the same transaction.
| Score | Category | anomalous |
|---|---|---|
| < 0.5 | normal | no |
| 0.5 – 0.6 | suspicious | yes |
| 0.6 – 0.8 | elevated | yes |
| ≥ 0.8 | high_risk | yes |
Try it without sending anything:
curl -s -X POST "$GATEWAY/api/v1/sentinel/score" -H 'Content-Type: application/json' \
-d '{"value":45000000,"gas_price":2,"nonce":3,"last_nonce":2,"balance":48000000,
"tx_count_last_10":1,"recipient_tx_count":0,"is_self_transfer":false}'
# {"anomalous":true,"category":"elevated","score":0.7119}What happens to a transfer
Always on live
sentinel_enabled = 1: every transfer is scored when it reaches a node.- Score ≥
sentinel_reject_threshold(0.85) → refused with HTTP 400. - Anomalous but below that → accepted; the response carries
sentinel_score,sentinel_categoryandsentinel_warning, and the gateway broadcaststx_anomaly.
Policy layer off by default
With sentinel_policy_enabled = 1, a transfer that would otherwise apply can be held: the sender is debited, the recipient is not credited yet.
| Band | Condition | What happens |
|---|---|---|
| Trusted | Recipient is on the sender's active trust list | Applied normally |
| Hold | Score ≥ sentinel_hold_threshold (0.60) | Released automatically after sentinel_hold_blocks (50); the sender may cancel before that |
| Step-up | Score ≥ sentinel_stepup_threshold (0.70), or signed by a session key with score ≥ sentinel_agent_confirm_threshold (0.60) | Waits for the owner to confirm with the master key; refunded if not confirmed within sentinel_confirm_expiry_blocks (1 000) |
Held transfers have status held; they end as confirmed (released), cancelled or refunded. On cancel or refund the value returns to the sender; the fee is kept.
| Action | Endpoint |
|---|---|
| List held transfers | GET /api/v1/held/:address |
| Confirm (owner, master key) | POST /api/v1/transactions/:hash/confirm |
| Cancel (owner) | POST /api/v1/transactions/:hash/cancel |
WebSocket events: tx_held, tx_released, tx_cancelled.
Trust list
An owner can mark recipients as trusted; transfers to them skip the hold. A new entry becomes active only after trust_activation_blocks (300) — so a stolen key cannot add an attacker's address and use it at once.
POST /api/v1/trust/add, POST /api/v1/trust/remove, GET /api/v1/trust/:owner.
Session keys can't approve themselves
Anything aimed at neuro:system.trust — trust-list changes, confirming or cancelling a held transfer — is refused for session keys. An AI agent can start a risky transfer; only the owner's master key can let it through. See Account abstraction.
Calibration experimental
The model is trained on synthetic transaction patterns. Real transfers score higher than the synthetic data suggested: on a test network a plain 1.5 NRO transfer to a new recipient scores about 0.62, and measured scores rarely exceed about 0.74. Two consequences:
- The 0.85 reject threshold is effectively never reached.
- With the default thresholds, turning the policy layer on would hold many ordinary transfers to new recipients.
Thresholds are governance parameters and will be tuned, and the model retrained, on real transaction data before the policy layer is enabled on a public network.
How it is tested: the policy layer passed a seven-step test on four validators with a quorum of three — automatic release, step-up confirmation through a different node, refund at expiry, cancellation through a third node, surviving a node restart, trust-list activation, and identical counters and state roots on every node. Node unit tests cover each branch.
Metrics
Prometheus counters, kept in chain state so every node reports the same numbers: nc_sentinel_flagged_total{category}, nc_sentinel_held_total, nc_sentinel_released_total{how}.