本文へスキップ
Anon Wallet

RAILGUN稼働状況履歴API

稼働状況APIは、「Anonのモニターは今何を見ているか」に答えます。このエンドポイントは、「過去2週間に何を見てきたか」に答えます。ライブレポートに載っているすべてのサービスとチェーンについて、最大14日分を5分バケットごとに1文字で返し、直近24時間・7日・14日の稼働率も返します。ステータスページの棒グラフを描くには十分です。誰でも読み取れます。APIキーもアカウントも不要です。

GET https://api.anon.inc/api/v1/railgun/liveness/history

これはAnon独自のモニターが1か所から見た状況です。Railgunの公式ステータスではなく、Railgun自体の測定でもありません。すべての文字は、Anonのモニターが公開したステータスです。このデータからわからないことは限界にまとめています。

ベータ版: このAPIはベータ版で、ライブのエンドポイントと同じ約束をします。フィールド、値、レスポンスの形は予告なく変更されることがあります。未知のフィールドや値は無視するようにクライアントを作ってください。ベータ版についてを参照してください。

プロパティ 値
ベースURL https://api.anon.inc
認証 なし
CORS すべてのオリジン、認証情報なし(GET、HEAD、OPTIONS)
ステータス ベータ版:予告なく変更されることがあります(ベータ版について)
形式 JSON、schemaVersion 1(JSON Schema)
粒度 5分バケット、最大14日
キャッシュ ブラウザは60秒、共有キャッシュは120秒

クイックスタート

curl -i https://api.anon.inc/api/v1/railgun/liveness/history

レスポンスには履歴とキャッシュヘッダーが含まれます。

HTTP/2 200
content-type: application/json; charset=utf-8
cache-control: public, max-age=60, s-maxage=120, stale-while-revalidate=60, stale-if-error=300
etag: "5b3f0c9a1d7e4426"
access-control-allow-origin: *
access-control-expose-headers: ETag

ETagをIf-None-Matchとして送り返すと、変更があったかどうかを尋ねられます。変更がなければ、本文のない304 Not Modifiedが返ります。

curl -i https://api.anon.inc/api/v1/railgun/liveness/history \
  -H 'If-None-Match: "5b3f0c9a1d7e4426"'

クエリパラメーターはありません。リクエストのたびに保存済みの履歴全体が返ります。文字列の大半は同じ文字の繰り返しなので、圧縮後は数KBです。

小さなTypeScriptのクライアント例です。1行を読み取り、バケットを一定本数の棒にまとめ、稼働率を整形します。nullの稼働率は100%ではなく「データなし」と表示し、未知の文字はデータなしとして扱います。

type Uptime = Record<"24h" | "7d" | "14d", number | null>;

interface HistoryRow {
  service: string;
  chainId: number | null; // null for waku
  buckets: string; // one character per bucket, oldest first
  uptime: Uptime;
}

interface History {
  schemaVersion: number;
  generatedAt: string;
  bucketSeconds: number;
  retentionDays: number;
  from: string;
  to: string;
  rows: HistoryRow[];
}

const res = await fetch("https://api.anon.inc/api/v1/railgun/liveness/history", {
  headers: { Accept: "application/json" },
  signal: AbortSignal.timeout(8000),
});
if (!res.ok) throw new Error(`liveness history: HTTP ${res.status}`);
const report: History = await res.json();
if (report.schemaVersion !== 1) throw new Error("liveness history: unknown schemaVersion");

// Worst known state wins. Anything else in a bar is only shown if nothing known is.
const SEVERITY: Record<string, number> = { c: 0, k: 1, d: 2, u: 3 };

function barState(chars: string): string {
  let worst = "";
  for (const ch of chars) {
    if (ch in SEVERITY && (worst === "" || SEVERITY[ch] > SEVERITY[worst])) worst = ch;
  }
  if (worst) return worst;
  if (chars.includes("?")) return "?"; // unknown: no evidence
  if (chars.includes(".")) return "."; // unmonitored
  return "-"; // no data, or a character this client does not know
}

// Split the buckets into `bars` groups of nearly equal size, oldest first. Fewer
// buckets than bars gives one bar per bucket.
function downsample(buckets: string, bars: number): string[] {
  const count = Math.min(bars, buckets.length);
  const out: string[] = [];
  for (let i = 0; i < count; i++) {
    const start = Math.floor((i * buckets.length) / count);
    const end = Math.floor(((i + 1) * buckets.length) / count);
    out.push(barState(buckets.slice(start, end)));
  }
  return out;
}

// The server rounds to 4 decimals, so this is exact. null means "not enough data",
// which is not the same as 0% or 100%.
function formatUptime(value: number | null | undefined): string {
  if (typeof value !== "number" || !(value >= 0 && value <= 1)) return "no data";
  return `${(Math.round(value * 10000) / 100).toFixed(2)}%`;
}

for (const row of report.rows) {
  const label = `${row.service} ${row.chainId ?? "-"}`;
  console.log(label, downsample(row.buckets, 90).join(""), formatUptime(row.uptime["14d"]));
}

行はserviceとchainIdで対応づけ、使わない行は無視してください。行の順序には頼らないでください。

エンドポイント

GET https://api.anon.inc/api/v1/railgun/liveness/history

  • 認証もパラメーターもありません。クエリ文字列は無視されます。
  • HEADはヘッダーだけを返します。OPTIONSはCORSプリフライトに応答します。
  • 本文はapplication/json; charset=utf-8です。
  • 1回のレスポンスで、すべてのチェーンのすべてのサービスをカバーします。1回取得してから絞り込んでください。
  • 行は/api/v1/railgun/livenessのライブレポートと同じもので、順序も同じです。現在は21行です。各サービスの内容はサービスとチェーンを参照してください。

レスポンス例

5行に絞った履歴で、値は説明用です。カバーするのは2時間だけで、エンドポイントが記録を始めた直後に返る内容に相当します。実際のレスポンスはすべての行を含み、bucketsの文字列は4033文字まで伸びます(履歴の長さ)。

{
  "schemaVersion": 1,
  "generatedAt": "2026-10-06T14:03:11.402Z",
  "bucketSeconds": 300,
  "retentionDays": 14,
  "from": "2026-10-06T12:00:00Z",
  "to": "2026-10-06T14:00:00Z",
  "rows": [
    {
      "service": "ppoi",
      "chainId": 1,
      "buckets": "cccccccccccccccckcccccccc",
      "uptime": { "24h": 0.96, "7d": 0.96, "14d": 0.96 }
    },
    {
      "service": "subsquid",
      "chainId": 137,
      "buckets": "cccccckkkkdddcccccccccccc",
      "uptime": { "24h": 0.72, "7d": 0.72, "14d": 0.72 }
    },
    {
      "service": "subsquid",
      "chainId": 11155111,
      "buckets": ".........................",
      "uptime": { "24h": null, "7d": null, "14d": null }
    },
    {
      "service": "broadcasters",
      "chainId": 137,
      "buckets": "cccccccccccccuccccccccccc",
      "uptime": { "24h": 0.96, "7d": 0.96, "14d": 0.96 }
    },
    {
      "service": "waku",
      "chainId": null,
      "buckets": "cccccc------ccccccccccccc",
      "uptime": { "24h": 1, "7d": 1, "14d": 1 }
    }
  ]
}
  • fromは2026-10-06T12:00:00Z、toは2026-10-06T14:00:00Zです。間隔が24個あるので、すべてのbuckets文字列は25文字で、バケットごとに1文字、古いものから順に並びます。toは最後のバケットの開始時刻で、generatedAt(14:03)の時点ではまだ埋まりつつありました。
  • ppoi行にはkバケットが1つあります。その5分間のうち少なくとも1分間、サービスはcatching-upで、それより悪い状態はそれほど長く続きませんでした。稼働率は、既知の25バケットのうち24で0.96です。
  • Polygon(137)のsubsquid行には、kバケットが4つ、続いてdバケットが3つ連続し、その後回復しています。25バケット中18がcなので、稼働率は0.72です。
  • Sepoliaのsubsquid行はすべて.で、監視対象外です。既知のバケットが12未満なので、稼働率はすべてnullです。
  • broadcasters行にはuバケットが1つあります。その5分間のうち少なくとも1分間、サービスはunavailableでした。
  • waku行には-バケットが6つあります。モニターが動いていなかったか、何も記録しませんでした。これらは稼働率から除外され、既知の19バケットで稼働率は1です。
  • ここで3つの稼働率の値が同じなのは、履歴が2時間分しかないためです。各ウィンドウは、実際にあるデータに合わせて切り詰められます。
  • chainIdは、唯一の共有行であるwakuではnullです。

フィールド

履歴

フィールド 型 意味
schemaVersion 整数 現在は1です。ベータ版の間は変わることがあります。
generatedAt タイムスタンプ 保存済みの履歴が最後に書き込まれた時刻です。配信された時刻ではありません。約1分ごとに進みます。
bucketSeconds 整数 バケット1つの長さです。現在は300です。
retentionDays 整数 保持する履歴の最大日数です。現在は14です。
from タイムスタンプ buckets[0]の開始時刻で、300秒の境界に揃っています。
to タイムスタンプ 最後のバケットの開始時刻で、そのバケットはまだ埋まりつつあります。
rows 配列 サービスと範囲ごとに1つのオブジェクトで、順序はライブレポートと同じです。

fromとtoはUTCで、末尾は文字どおりのZ、小数秒はありません。YYYY-MM-DDTHH:mm:ssZの形式です。generatedAtは、ライブレポートと同じくミリ秒精度です。

すべてのbuckets文字列の文字数は(to − from) / bucketSeconds + 1で、全行で同じです。

行

フィールド 型 意味
service 文字列 現在はppoi、indexer、subsquid、broadcasters、wakuです。今後増える可能性があります。意味はライブレポートと同じです。
chainId 整数またはnull 正のチェーンIDです。現在、wakuは共有の1つのチェックなのでnullです。
buckets 文字列 fromからtoまで(両端を含む)、バケットごとに1つの文字で、古いものから順に並びます。
uptime オブジェクト 24h、7d、14dで、それぞれ0から1の数値またはnullです。稼働率を参照してください。

Sepolia(11155111)はテストネットです。ライブレポートと同様に、自分で計算するメインネットの数値には含めないでください。

文字

文字 公開されたステータス 意味
c current サービスが応答し、追いついていました。
k catching-up サービスは応答しましたが、遅れていました。障害ではありません。
d degraded サービスは深刻に機能が低下していました。
u unavailable Anonのモニターは、3回連続のプローブで使える応答を得られませんでした。
? unknown そのバケットには使える根拠がありませんでした。
. unmonitored Anonはこのチェックを監視していません。
- なし データなし:モニターが動いていなかったか、そのバケットのサンプルを記録しませんでした。

各ステータスの意味はライブレポートと同じです。ステータスを参照してください。この集合は増える可能性があります。知らない文字は、そのバケットに限って-として扱い、行の残りはそのまま使ってください。

?、.、-は、互いに異なり、かつ明らかに緑ではない見た目で描いてください。この3つは、根拠なし、対象外、記録なしという別々の意味です。

バケットと稼働率の計算方法

バケット

モニターは、すべての行についておよそ30秒ごとにステータスを公開します。履歴にはこの公開されたステータスが保存され、バケットの文字は、その5分間にその行について公開されたものから決まります。

  1. 既知のステータス、つまりcurrent、catching-up、degraded、unavailableを取り出します。深刻度はuが最も高く、d、k、cの順に低くなります。文字は、その5分間に合計で少なくとも1分続いた最も悪いステータスです。それより悪いステータスだった時間もそこに含めて数えます。たとえばdegradedが30秒、unavailableが40秒ならdになります。再起動のような、それより短い一時的な乱れは数えず、バケットは代わりにcを示します。
  2. 既知のものがなくunknownだけがある場合、文字は?です。
  3. unmonitoredのステータスしかない場合、文字は.です。
  4. サンプルがまったくなければ、文字は-です。

したがって、uはそのバケットのうち少なくとも1分間サービスがunavailableだったことを意味し、cはcurrentより悪い状態が1分続かなかったことを意味します。バケットは平均ではなく、続いた中で最も悪いステータスを表します。最後のバケットはまだ埋まりつつあるため、終わるまで文字が変わることがあります。

稼働率

ウィンドウごとの稼働率は次のとおりです。

c / (c + k + d + u)

直近24時間、7日、14日のバケットを対象に数え、実際にある履歴に合わせて切り詰めます。結果は小数第4位に丸めます。

  • ?、.、-のバケットは、分子と分母の両方から除外されます。成功にも失敗にも数えません。
  • kは、dやuと同じく稼働率を下げます。追いついている途中のサービスは稼働中とは数えません。
  • ウィンドウの既知のバケットが12未満(根拠が1時間分に満たない)の場合、値はnullです。まったく監視されていない行もnullです。
  • nullは0でも1でもありません。「データなし」と表示してください。
  • 値がちょうど1なら、そのウィンドウの既知のバケットにc以外のものがなかったことを意味します。14日間でc以外のバケットが1つあるだけで、値は最大でも0.9998になります。

bucketsから、独自のウィンドウやサービスの組み合わせで計算できます。自分で計算した値は、ウィンドウの端でサーバーの値と少し異なることがあります。

履歴の長さ

fromは、モニターが最初に記録したバケット、または14日ウィンドウの開始のうち、遅いほうです。履歴は、この機能が始まった日からしか存在せず、さかのぼっての補完はありません。そのため、文字列は1文字から始まり、5分ごとに1文字ずつ増えて、最大4033文字になります。最初の14日かかります。その後はウィンドウが移動し、新しいバケットが始まるたびに最も古いバケットが外れます。

それまでは、24h、7d、14dが同じ短い期間を指すことがあり、履歴が浅いと7dや14dがnullになることもあります。裏付けとなるデータ量を示さずに、数値に「14日」とラベルを付けないでください。fromとtoを比べればわかります。

限界

  • 観測地点は1つです。 Anonのインフラで動くAnonのモニターが、Anonが使うよう設定されたサービスをプローブしています。あなたから見たそれらのサービスの状況は異なることがあります。バケットは、あなたのウォレットやネットワークが何を見たはずかを示すものではありません。
  • モニターが公開した内容であり、Railgunの測定ではありません。 uバケットは、モニターが読むよう設定されたサービスから、3回連続で使える応答を得られなかったことを意味します。ほかの誰にとってもそのサービスが停止していたことの証明にはなりません。「安全に取引できる」ことを示すシグナルでもありません。わからないことを参照してください。
  • 履歴は開始時点からです。 最初に記録されたバケットより古いものは存在せず、さかのぼっての補完もありません。履歴が短いのは、完璧だからではなく、単に短いからです。
  • データなしは、監視の空白です。 -バケットは、障害でも成功でもありません。その5分間、モニターが動いていなかったか、何も記録しなかったことを意味するので、空白として表示してください。稼働率からは除外されます。
  • 不明も障害ではありません。 ?は、モニターに根拠がなかったことを意味します。稼働率からは除外されます。
  • 14日分だけです。 それより古いバケットは消えます。もっと長い記録が必要なら、レスポンスを自分で保存してください。
  • 粗い情報です。 5分バケットは、それより短い出来事を隠します。合計1分未満の問題はまったく表示されず、1分続いた問題はバケット全体の色を変えます。稼働率もその両方を引き継ぎます。
  • ベータ版。 ライブのエンドポイントと同様に、予告なく変わることがあります。ベータ版についてを参照してください。

鮮度とキャッシュ

履歴の変化はゆるやかです。最新のバケットはサンプルが届くにつれて変わることがあり、履歴が14日分になってからは、5分ごとにウィンドウがバケット1つ分ずつ進みます。レスポンスには次が含まれます。

Cache-Control: public, max-age=60, s-maxage=120, stale-while-revalidate=60, stale-if-error=300
ディレクティブ 効果
max-age=60 ブラウザーはコピーを60秒間再利用します。
s-maxage=120 CDNなどの共有キャッシュはコピーを2分間再利用します。
stale-while-revalidate=60 キャッシュは、その後さらに最大1分間、新しいコピーを取得しながら古いコピーを返すことがあります。
stale-if-error=300 オリジンがエラーを返した場合、キャッシュは最大5分間コピーを返し続けることがあります。

どれくらい古くなりうるか。 generatedAtは、保存済みの履歴が最後に書き込まれた時刻を示します。それに加えて、エッジのコピーは手元に届く時点で数分古くなっていることがあります。s-maxageで最大2分、stale-while-revalidateで最大1分、ブラウザーではさらに最大1分です。このエンドポイントを、サービスが今稼働しているかどうかの判断に使わないでください。それにはライブのエンドポイントを使い、そのexpiresAtを読んでください。

1分に1回より頻繁にポーリングしないでください。 それより速く取得しても、キャッシュ済みのコピーが返るだけです。チャートを表示するページは、開いたときに1回取得し、数分後に表示されているときにもう一度取得すれば十分です。

ETagと304。 ETagは本文の引用符付きハッシュです。モニターは約1分ごとに履歴を書き直し、generatedAtは本文の一部なので、どのバケットも変わっていなくてもgeneratedAtとETagは約1分ごとに変わります。304が返るのは、主に同じ1分以内に再検証したときです。If-None-Matchを使うと、変更のない履歴は304で済みます。この応答にはETag、Cache-Control、CORSヘッダーが含まれ、本文はありません。レスポンスはVary: Originを送りません。CDNはOriginリクエストヘッダーの値ごとに別々のコピーを保持し、クエリ文字列は無視します。

HTTPステータスコードとエラー

ステータス 発生条件 備考
200 履歴です。 200は履歴が読み取れることを意味し、サービスが正常であることを意味しません。
304 If-None-Matchが一致しました。 本文はありません。
204 OPTIONSプリフライトです。 CORSを参照してください。
405 それ以外のメソッドです。 Allow: GET, HEAD, OPTIONS。
503 履歴を利用できません。 最初の履歴が書き込まれるまで、または保存済みの履歴を5分を超えて読み取れないときに返ります。Retry-After: 30とCache-Control: no-storeが付きます。バックオフしながら再試行し、手元にある最後の正常な履歴を表示し続けてください。
404 それ以外のパスです。 ゲートウェイが応答します。
429 公正な利用のレート制限を超えました。 APIではなくCDN(Cloudflareのエラー1015)から返ります。約60秒続き、Retry-Afterは約60です。

分岐はHTTPステータスコードで行い、メッセージの文面では決して行わないでください。API自体のエラーは{"error": "<message>"}の形です。ゲートウェイの404とCDNの429は別の本文を使います。429はCloudflareのtext/plainページです。

ブラウザーでは429を読み取れません。 CDNの429ページにはAccess-Control-Allow-Originがないため、ブラウザーがブロックし、fetchはほかのネットワーク障害やCORS障害と同じくTypeErrorで拒否されます。コードからはステータスもRetry-Afterも見えません。ネットワーク障害は429や503と同じように扱ってください。バックオフし、最後に取得できた正常な履歴を、そのgeneratedAtを添えて表示し続けてください。

CORS

履歴は公開情報で、呼び出し元ごとの内容がないため、すべてのレスポンスがすべてのオリジンを許可します。

ヘッダー 値
Access-Control-Allow-Origin *
Access-Control-Expose-Headers ETag

Access-Control-Allow-Credentialsはありません。Acceptヘッダー付きの通常のfetchは単純リクエストで、プリフライトは不要です。自分でIf-None-Matchを設定するスクリプトにはプリフライトが必要で、エンドポイントは自身のOPTIONSプリフライトに204で応答します。許可されるメソッドとヘッダーはライブのエンドポイントと同じです。

公正な利用

ライブのエンドポイントと同じルールが適用されます。キーもSLAもなく、Cache-Controlに従ってETagを使い、サーバーは説明的なUser-Agentを送り、このルートへのリクエストが1 IPあたり毎分約120件を超えると429が返ります。429や5xxが返ったらバックオフしてください。

加えて、このデータはゆっくり変わるので、1分に1回より頻繁にポーリングせず、ユーザーがこれを表示する画面を開いたときに取得するのが望ましい使い方です。多くのユーザーに提供する場合は、自分のサーバーで取得し、そこでキャッシュしてください。

ベータ版について

このAPIはベータ版で、約束はライブのエンドポイントと同じであり、それより強くはありません。

  • 予告なく変わることがあります。 フィールド、文字、ウィンドウ、レスポンスの形は、予告期間なしに追加、名前変更、型変更、削除されることがあり、非推奨化や提供終了の手続きも約束しません。保持期間、バケットの長さ、稼働率のウィンドウは現時点の値です。
  • schemaVersionは変わることがあります。 知らない値であれば、更新するまで履歴を読み取れないものとして扱ってください。JSON Schemaは寛容に作られており、現時点のレスポンスを記述するもので、約束ではありません。
  • 変更履歴。 変更は、可能な範囲で下の変更履歴に記録します。

これに耐えられるクライアントの書き方は次のとおりです。

  • 未知のフィールドは無視してください。 履歴、各行、uptimeなど、すべての階層で無視します。
  • 閉じた集合に頼らないでください。 知らないバケット文字は、そのバケットに限って-として扱ってください。使わないserviceやchainIdの行は無視してください。知らないuptimeのキーも無視してください。
  • 履歴全体ではなく、1行だけを失敗させてください。 読み取れない行が1つあっても、たとえばuptimeがない、buckets文字列の長さが合わないといった場合でも、ほかの行まで空にしてはいけません。
  • 長さを固定で書かないでください。 from、to、bucketSecondsを使ってください。

変更履歴

2026-10-07

追加されました。GET /api/v1/railgun/liveness/historyは、ライブレポートのすべての行について、最大14日分の5分バケットと、24時間・7日・14日の稼働率を返します。schemaVersion 1、ベータ版です。履歴は2026-10-07 01:55 UTCから記録され、さかのぼっての補完はありません。