Agent Guide

Observing many signals. Resolving one view.

How to consume the Demos Network Oracle as a machine agent.

Purpose

The Oracle provides current network state for the Demos blockchain testnet. It is strictly watch-only: it observes, interprets, and publishes. It does not advise, predict, or issue instructions.

Your agent reads the state and decides what to do with it. The Oracle never tells you.

Endpoint

GET /organism

Returns 17 top-level JSON fields. No fleet data. Every top-level field is always present and never null. Most fields are flat values; risk_factors is an array of strings and last_24h is a small summary object (fields inside it may be null while history accumulates). Refreshes every 20 seconds.

Machine-readable contract: /organism/schema — JSON Schema with stability guarantees (additive-only within api_version 1.x) and changelog. Validate your integration against it.

{
  "status": "unknown",
  "trend": "unknown",
  "risk": "elevated",
  "data_quality": "insufficient",
  "confidence": "uncertain",
  "agreement": "unknown",
  "active_incidents": 0,
  "max_incident_severity": "none",
  "summary": "Insufficient data — fewer than 2 public nodes reachable",
  "status_reason": "Insufficient data to assess network state",
  "risk_factors": [
    "Only 1 of 5 public nodes reachable — limited cross-checking"
  ],
  "confidence_reason": "Insufficient reachable public nodes to cross-check",
  "agreement_reason": "Fewer than 2 reachable nodes",
  "staleness_seconds": 1,
  "last_updated": "2026-06-10T08:28:38.773Z",
  "api_version": "1.0",
  "last_24h": {
    "sufficient": true,
    "coverage_pct": 99.9,
    "observed_cycles": 4317,
    "expected_cycles": 4320,
    "typical_set_size": 5,
    "peak_set": null,
    "chain_movement": { "state": "unknown", "reason": "no_data" },
    "longest_non_stable_minutes": 0,
    "active_critical_public_incidents": 0,
    "computed_at": "2026-06-10T08:28:38.773Z"
  }
}

This is a real captured response. Note the honest-uncertainty values (unknown, insufficient, uncertain) — your parser must treat these as first-class states, not errors.

Field Meanings

status — network operability. stable degraded unstable unknown

risk — resilience / safety margin. low elevated high

confidence — certainty of assessment. clear uncertain

data_quality — observability. sufficient insufficient

agreement — validator consensus. strong moderate weak unknown

trend — direction of change. improving stable worsening unknown

active_incidents — count of current unresolved issues.

max_incident_severity — worst active incident: none info warning critical

staleness_seconds — seconds since last observation. Use for freshness judgment.

status_reason — one-sentence explanation of the current status.

risk_factors — array of strings; the concrete observations behind the current risk level.

confidence_reason — why confidence is at its current state.

agreement_reason — why agreement is at its current state.

last_24h — rolling 24-hour observability summary (coverage, observed vs expected cycles, chain movement, incident counts). Fields inside it may be null while history accumulates.

The Minimal Triple

The simplest consumption pattern reads three fields:

status + risk + confidence

These give you the complete picture in three values. Add data_quality for trust calibration and active_incidents for problem awareness.

Example: status: stable · risk: elevated · confidence: clear
Interpretation: network is operational, but redundancy is reduced.

Trust Calibration

Before relying on the assessment, check:

data_quality — if insufficient, treat all values with skepticism.

staleness_seconds — if high, data may not reflect current reality.

confidence — if uncertain, signals conflict. Consume with caution.

When data_quality is insufficient, status will be unknown and risk will be elevated. This is intentional — the Oracle treats missing data as a reason for caution.

Polling Guidance

Recommended interval: 10–30 seconds. The Oracle updates every 20 seconds.

Respect the Cache-Control: public, max-age=5 header. Do not poll faster than every 5 seconds.

For less time-sensitive consumers, polling every 60 seconds is sufficient.

Consumption Examples

curl

curl -s https://demos-oracle.com/organism | jq '{status, risk, confidence}'

JavaScript

const res = await fetch('https://demos-oracle.com/organism');
const state = await res.json();
if (state.status === 'unstable' || state.risk === 'high') {
  // handle degraded network
}

Python

import requests
state = requests.get('https://demos-oracle.com/organism').json()
if state['data_quality'] == 'insufficient':
    pass  # data unreliable, act cautiously

Other Endpoints

/health — full diagnostic with agreement details, public nodes, signals, reference data.

/incidents — incident log. Default scope: public. Use ?scope=all for fleet.

/commerce/observations — commerce infrastructure reachability (Layer 2, independent from the core network assessment).

/methodology — how the Oracle makes assessments.

/commerce/methodology — how commerce observation works.

Error Handling

The Oracle is designed to always return valid JSON with all fields present. However, your agent should handle network and edge-case failures gracefully.

HTTP errors — The Oracle may return 502/503 during restarts (typically <30 seconds). Retry with exponential backoff.

Stale data — If staleness_seconds exceeds 120, the Oracle may be restarting or experiencing issues. The data is still valid but may not reflect the current moment. Your agent should flag this.

Insufficient data quality — When data_quality is insufficient, the Oracle cannot see enough public nodes to form a credible assessment. All other fields are still present but should be treated as low-confidence. Status will be unknown and risk will be elevated.

Timeouts — Set a reasonable timeout (5–10 seconds). If the Oracle doesn't respond, your agent should fall back to its last known state or a safe default, not crash.

Never assume the Oracle is always available. Design your agent to degrade gracefully when the Oracle is unreachable or returning stale data.

Edge Cases

These state combinations are real and your agent should handle them explicitly:

status: stable + risk: elevated
The network is working, but resilience is reduced. Fewer nodes than ideal are reachable. The chain functions, but a single additional failure could cause degradation. Your agent should continue operating but with heightened caution.

status: unknown + data_quality: insufficient
The Oracle cannot see enough public nodes to assess the network. This is not "the network is down" — it is "the Oracle cannot tell." Your agent should not assume the worst. It should wait and retry, or fall back to the last known good state.

status: stable + confidence: uncertain
The Oracle has visibility but the signals are contradictory. Nodes may report different block heights or mixed health. The assessment says "stable" but with reduced certainty. Your agent should consume this data but flag it for human review.

agreement: unknown
Fewer than 2 public nodes are reachable, so consensus cannot be measured. This often accompanies data_quality: insufficient. It does not mean validators disagree — it means the Oracle cannot observe enough to tell.

trend: unknown
Not enough recent history to compute a trend. Common after Oracle restarts or data gaps. Trend resets to unknown until enough consecutive observations accumulate. This is normal, not an error.

staleness_seconds > 60
The Oracle's last observation is more than a minute old. Possible causes: Oracle restarting, high load, network issues between the Oracle and its data sources. The data is still the best available — it's just not fresh. Your agent should note this and poll again shortly.

Production Python Example

import requests
import time
import logging

ORACLE_URL = "https://demos-oracle.com/organism"
MAX_STALENESS = 120  # seconds
POLL_INTERVAL = 20   # seconds
MAX_RETRIES = 3

logger = logging.getLogger("oracle_consumer")

def fetch_oracle_state():
    # Fetch and validate Oracle state with error handling
    for attempt in range(MAX_RETRIES):
        try:
            r = requests.get(ORACLE_URL, timeout=10)
            r.raise_for_status()
            state = r.json()

            # Validate required fields exist
            required = ["status", "risk", "confidence",
                        "data_quality", "staleness_seconds"]
            for field in required:
                if field not in state:
                    logger.warning(f"Missing field: {field}")
                    return None

            return state

        except requests.exceptions.Timeout:
            logger.warning(f"Oracle timeout (attempt {attempt + 1})")
            time.sleep(2 ** attempt)
        except requests.exceptions.HTTPError as e:
            logger.warning(f"Oracle HTTP {e.response.status_code}")
            if e.response.status_code in (502, 503):
                time.sleep(5)  # Oracle restarting
            else:
                return None
        except Exception as e:
            logger.error(f"Oracle error: {e}")
            return None

    return None  # All retries exhausted

def assess_reliability(state):
    # Determine if the Oracle data is reliable enough to act on
    if state is None:
        return {"reliable": False, "reason": "Oracle unreachable"}

    if state["data_quality"] == "insufficient":
        return {"reliable": False,
                "reason": "Oracle has limited visibility"}

    if state["staleness_seconds"] > MAX_STALENESS:
        return {"reliable": False,
                "reason": f"Data is {state['staleness_seconds']}s old"}

    if state["confidence"] == "uncertain":
        return {"reliable": True,  # usable but flagged
                "reason": "Signals conflict — use with caution",
                "flagged": True}

    return {"reliable": True, "reason": None}

# Usage
state = fetch_oracle_state()
check = assess_reliability(state)
if check["reliable"]:
    # Safe to use state["status"], state["risk"], etc.
    pass
else:
    logger.info(f"Oracle data unreliable: {check['reason']}")
    # Fall back to last known state or safe default

Combining Endpoints

For richer context, combine multiple Oracle endpoints:

# Minimal: just the state
GET /organism

# Detailed: state + diagnostic data
GET /organism  →  status, risk, confidence
GET /health    →  agreement details, node counts, signals
GET /incidents →  specific active problems

# Commerce: attestation authority reachability
GET /commerce/observations  →  authority states (Layer 2, separate from the core network assessment)

Important: /commerce/observations is Layer 2 data. It reports whether attestation authority endpoints are reachable. It does not influence and is not influenced by the network assessment in /organism. These are independent observation layers.

Commerce API (Layer 2)

The Oracle also monitors attestation authority endpoints used by the Demos agent commerce protocol. This is a separate observation layer.

GET /commerce/observations
{
  "layer": "commerce_intelligence",
  "network_context": "testnet",
  "commerce_observability_state": "healthy",
  "authorities_observed": 10,
  "authorities_healthy": 10,
  "authorities": [
    {
      "authority_id": "gleif",
      "name": "GLEIF",
      "category": "public_api",
      "reachability": "healthy",
      "freshness": "fresh",
      "observed_at": "2026-05-18T..."
    }
  ]
}

commerce_observability_state — overall: healthy degraded partial unavailable unknown

reachability — per authority: healthy degraded unavailable unknown

Layer separation: Commerce data is Layer 2. It never affects /organism status, risk, trend, agreement, confidence, or data quality. These are architecturally independent observation layers.

Full commerce methodology: /commerce/methodology

HTTP Status Codes

200 — Success. Valid JSON response.

301 — Redirect (e.g., /home/).

401 — Unauthorized. Admin endpoints require ?token= parameter.

404 — Endpoint not found.

502/503 — Oracle restarting. Retry after 5–10 seconds.

All public endpoints return JSON with Content-Type: application/json and Access-Control-Allow-Origin: * (CORS enabled).

Design Principle

This API describes state. It does not suggest actions.
The Oracle publishes its core assessment. Your agent interprets it.
DNO informs context; it does not advise, predict, score, certify, or decide action.