エッジ: 分析、エッジ設定、KV、AI ゲートウェイ、段階的リリース、ミドルウェア
サイトの前で動くもの: 個人情報なしのウェブ分析と速度測定、デプロイせずに変える値と機能フラグ、キー・バリューストア、OpenAI 互換の AI ゲートウェイ、新しいデプロイを一部の訪問者に先に届けること、middleware.ts。
エッジ
サイトと API の前で動くものです。コンソールの分析・エッジ画面、管理 REST、下のツールで扱います。
ウェブ分析と速度インサイト
get_analytics 訪問、参照元、国、デバイスの合計
get_speed_insights Core Web Vitals の75パーセンタイル
set_insights オン・オフ (オーナーと管理者)
get_analytics_breakdown キャンペーン(UTM)、ルート、カスタムイベントとプロパティ、除外したボット
set_analytics_previews プレビューの訪問を数える(オーナーと管理者)
export_analytics 合計を CSV で
list_analytics_reports カスタムレポート一覧
get_analytics_report カスタムレポート 1 つ
save_analytics_report カスタムレポートの作成・変更(開発者以上)
remove_analytics_report カスタムレポートの削除オンにするとサイトの HTML に /_oneend/insights/script.js が入ります。Cookie もブラウザのストレージも使わず、IP アドレスとブラウザの情報は保存せず、合計だけを残します。訪問者は1日だけ区別します。
Vercel の @vercel/analytics・@vercel/speed-insights を使うアプリは、コードを変えずに同じ場所で集計されます。すべてのサイトが /_vercel/insights/script.js・/_vercel/speed-insights/script.js と報告先のパスを、サイトのファイルや rewrite より先に応答します。オフの間、そのスクリプトは何も送りません。カスタムイベントは oneend.track('Signup', { plan: 'pro' }) か @vercel/analytics の track() で送り、名前とプロパティの値ごとに数えます。最初のページのアドレスの utm_source・utm_medium・utm_campaign とフレームワークのルート(/blog/[slug])も数えます。人ではない訪問(AI クローラー、検索エンジン、その他のプログラム)は数えず、除外した数だけを残し、計量しません。プレビューと dpl- アドレスの訪問は、set_analytics_previews でオンにするまで数えません。
イベントは analytics_events として計量されます。国は運営者が指定したヘッダーかアドレス範囲の表からだけ読みます。どちらもないサーバーでは国を推測せず、??として数え、レポートの note でそう伝えます。
エッジ設定と機能フラグ
get_edge_config 全体とバージョン、上限
set_edge_config キーの保存・削除 (開発者以上、履歴が残る)
get_edge_config_history 誰がいつ何を変えたか
list_edge_config_tokens 読み取りトークンと連携したプロジェクト
create_edge_config_token 読み取りトークンを作成(管理者、一度だけ表示、すぐに連携も可)
connect_edge_config_token 別のプロジェクトのデプロイに変数として連携
disconnect_edge_config_token 連携を解除
revoke_edge_config_token トークンを失効
list_flags フラグとエッジ設定のフラグをまとめて、どちらが決めるか
evaluate_flag アプリが受け取る値(コンテキスト、環境)
set_flag_enabled 1 つの環境でオン・オフ
get_flag フラグ1つのバリアント、環境ごとの設定とルール
create_flag フラグを作成(すべての環境でオフ)
update_flag 説明とバリアントを変更(管理者)
set_flag_environment 1つの環境のオン・オフ、オフのときのバリアント、既定で返すもの(serve)
add_flag_rule ルールを追加(条件1〜3個、serve)
move_flag_rule ルールを並べ替え(up | down)
remove_flag_rule ルールを削除
set_flag_archived アーカイブ・元に戻す(管理者)
list_flag_segments セグメント一覧
create_flag_segment セグメントを作成
update_flag_segment セグメントの条件、含める・除く id(管理者)
remove_flag_segment セグメントを削除(管理者、使うルールがないとき)
list_flag_sdk_keys /flags/v1/evaluate を呼ぶキー
create_flag_sdk_key SDK キーを作成(server | client、一度だけ表示)
revoke_flag_sdk_key SDK キーを廃止
get_remote_cache Turborepo リモートキャッシュ: 容量、ヒット、今月の計量(remote_cache_gb_hour)
clear_remote_cache リモートキャッシュを空にする(開発者以上)
list_remote_cache_tokens リモートキャッシュのトークンと turbo に渡す api、team
create_remote_cache_token トークンを作成(1回だけ表示、開発者以上)
revoke_remote_cache_token トークンを無効化
get_queue_metrics キューの記録: キューごとの待機数と最長待ち時間、時間ごとの送信数と処理数数秒以内にすべてのサーバーに反映されます。関数とミドルウェアは ctx.config.get(key)、ほかのサーバーは secret キーで GET https://<ref>.<zone>/config/v1 を呼び出します。
機能フラグは値の形で決まります: true、{"enabled": true, "percent": 20}、{"enabled": true, "allow": ["user-1"]}。ctx.flags.isEnabled(key, userId) は同じユーザーに常に同じ答えを返します。
コンソールの機能フラグ(ルール、セグメント、環境、SDK キー)とエッジ設定のフラグは一度に読まれます。順番: 同じキーのフラグ(アーカイブされていないもの)が決め、なければエッジ設定の値が決め、どちらもなければ見つかりません(isEnabled は false)。ctx.flags、OneEnd.flags、SDK キーで呼ぶ /flags/v1/evaluate、secret キーで呼ぶ /config/v1/flags/<key> はすべてこの順番に従い、答えの source(flag、edge_config)がどちらで決まったかを示します。ctx.flags.evaluate(key, { user: { id } }) はフラグの値とバリアントを返します。
同じ組織の別のプロジェクトもこのエッジ設定を読めます。読み取りトークン(bwec_…)を作ってそのプロジェクトに連携すると、そのプロジェクトの次のデプロイから GLOBAL_CONFIG(名前は選べます)に接続文字列 https://<ref>.<zone>/config/v1?token=… が入ります。トークンは読むだけで、失効させるとすぐに止まります。本番への連携は、そのプロジェクトのオーナー・管理者だけができます。
段階的リリース
get_rolling_releases 設定と進行中のリリース
set_rolling_releases 段階の割合、長さ、自動進行、自動中止の基準
advance_rolling_release 次の段階へ
promote_rolling_release すぐに全員へ
abort_rolling_release 中止して以前のデプロイへオンにすると、新しい本番デプロイは段階の割合 (例: 10%、50%) の訪問者だけを受け取ります。訪問者は __oneend_rr Cookie でどちらかに固定されます。
新しいデプロイの5xx率が基準を超えると、ゲートウェイが自分でリリースを中止し、以前のデプロイに戻します。以前のデプロイに rollback しても中止されます。
KV ストア
list_kv_keys キーの一覧 (シークレットを見られるロール)
get_kv 値1つ
set_kv 保存 (ttl は秒)
delete_kv 削除関数では ctx.kv.get/set/del/incr/expire/ttl/list を使います。REST は secret キーで https://<ref>.<zone>/kv/v1 です。Upstash の形式なので @upstash/redis をそのまま使えます:
import { Redis } from '@upstash/redis'
const redis = new Redis({ url: 'https://<ref>.<zone>/kv/v1', token: process.env.ONEEND_SECRET_KEY })
await redis.set('session:abc', 'payload', { ex: 60 })リクエストは kv_operations として、保存したバイトはデータベース容量として計量されます。値1つと全体のサイズはプランが決めます。
AI ゲートウェイ
get_ai_gateway アドレス、設定、プロバイダー、30日の使用量
set_ai_gateway オン、ユーザーの呼び出し、キャッシュ、1分の上限、プラットフォームへの切り替え
add_ai_provider プロバイダーを追加 (キーは封印)
set_ai_provider_enabled プロバイダーのオン・オフ
remove_ai_provider プロバイダーを削除
get_ai_gateway_logs 最近の呼び出し
set_ai_budgets プロジェクトの予算(ドル、日・週・月)
list_ai_gateway_keys ゲートウェイキー一覧
create_ai_gateway_key アプリ 1 つに渡すキー(予算付き、一度だけ表示)
set_ai_gateway_key_budgets キーの予算を変更
set_ai_gateway_key_limits キーの 1 分あたりのリクエスト数・トークン数
set_ai_model_aliases モデルの別名(blackall-air -> プロバイダーのモデル、システム指示付き)
set_ai_gateway_hooks キーの呼び出しの前に問い合わせ、後に知らせるフック(署名付き)
revoke_ai_gateway_key キーを無効化
get_ai_spend 支出: 今日・週・月、日別・モデル別・キー別import OpenAI from 'openai'
const ai = new OpenAI({ baseURL: 'https://<ref>.<zone>/ai/v1', apiKey: process.env.ONEEND_SECRET_KEY })
await ai.chat.completions.create({ model: 'openai/gpt-4o-mini', messages: [{ role: 'user', content: 'hi' }] })OpenAI クライアントをこのアドレスと secret キーで使います。provider/model はそのプロバイダーだけを、model だけなら優先順位どおりに呼び出し、失敗 (接続失敗、429、5xx) すると次に移ります。
プロバイダーのアドレスは公開された https のみです。プライベート、内部、メタデータのアドレスとリダイレクトは拒否します。プロジェクトのキーはプロバイダーに送りません。
リクエストは ai_gateway_requests、プラットフォームのプロバイダー (DeepSeek) のトークンは ai_gateway_tokens として計量されます。自分のキーで呼んだトークンはそのプロバイダーが請求します。
プロバイダーの種類は openai、anthropic、google、deepseek、openai-compatible です。Anthropic は Messages API と相互に変換して呼び出し(ストリームとツール呼び出しを含む)、Google は Gemini の OpenAI 互換アドレスで呼び出します。
アプリにはプロジェクトの secret キーの代わりにゲートウェイキー(bwai_…)を渡せます。キーごと、そしてプロジェクト全体に日・週・月の予算(ドル、UTC)を設定すると、使い切った後の呼び出しはプロバイダーに送る前に 429 budget_exceeded で拒否されます。呼び出しごとに費用(x-oneend-cost-usd、記録の cost_usd)と最初のトークンまでの時間(ttft_ms)を残します。自分のキーで呼んだ費用はプロバイダーの定価で計算した推定です。
画像と音声: 同じアドレスで /ai/v1/images/generations(画像)、/ai/v1/audio/speech(読み上げ)、/ai/v1/audio/transcriptions(文字起こし、multipart のファイル)も受け付けます。OpenAI SDK の images.generate・audio.speech.create・audio.transcriptions.create がそのまま使えます。プラットフォームのプロバイダーは画像も音声も作らないため、OpenAI か OpenAI 互換の GPU アドレス(RunPod・Together など)を add_ai_provider で追加します。ユーザーごとの上限と記録は会話と同じです。
アプリのユーザーごとの上限: set_ai_gateway の user_daily_requests・user_daily_tokens で、アプリのユーザー1人が1日(韓国時間0時区切り)に使えるリクエストとトークンを決めます。超えると 429 user_limit を返します。ユーザーはログインしたユーザーの id で、サーバーから secret キーで呼ぶときは x-oneend-user ヘッダー(または本文の user)にその人の id を入れます。自分のキーで呼んだトークンもユーザーごとに数えます。よく使ったユーザーは get_ai_gateway の top_users_today・top_users_30d に出ます。
ミドルウェア
get_middleware 実行するかと時間制限
set_middleware オン・オフと時間制限 (オーナーと管理者)import { NextResponse } from 'next/server'
export function middleware(request) {
if (!request.cookies.get('session')) return NextResponse.redirect(new URL('/login', request.url))
return NextResponse.next({ headers: { 'x-frame-options': 'DENY' } })
}
export const config = { matcher: ['/app/:path*'] }Next.js の middleware.ts (または proxy.ts) はビルド時にまとめられ、リクエストごとに最初に実行されます。ほかのフレームワークは blackwall.json に "middleware": "middleware.ts" と指定します。
関数と同じ隔離環境で実行し、next/server の NextResponse (next、rewrite、redirect、json、cookies) を使えます。rewrite は同じサイトのパスにだけできます。失敗したり時間を超えたりすると、ページは表示しません。
ミドルウェアの中では @vercel/edge-config の get() か OneEnd.config.get() でエッジ設定を、OneEnd.kv で KV を読みます。
実行回数は middleware_invocations、起動している間のメモリは runtime_gb_hour として計量されます。
REST
管理 REST:
GET /v1/projects/<ref>/analytics?days=7
GET /v1/projects/<ref>/analytics/breakdown?days=7&event=Signup
PUT /v1/projects/<ref>/analytics/previews
GET /v1/projects/<ref>/analytics/export?days=30
GET /v1/projects/<ref>/analytics/reports
POST /v1/projects/<ref>/analytics/reports
GET /v1/projects/<ref>/analytics/reports/<id>
PATCH /v1/projects/<ref>/analytics/reports/<id>
DELETE /v1/projects/<ref>/analytics/reports/<id>
GET /v1/projects/<ref>/speed-insights?days=7&device=mobile
PUT /v1/projects/<ref>/insights
GET /v1/projects/<ref>/edge-config
GET /v1/projects/<ref>/flags
GET /v1/projects/<ref>/flags/evaluate?key=promo&environment=production&context={...}
PUT /v1/projects/<ref>/flags/<key>/environments/<env>
GET /v1/projects/<ref>/remote-cache
DELETE /v1/projects/<ref>/remote-cache/artifacts
GET /v1/projects/<ref>/queues/metrics?hours=24
PATCH /v1/projects/<ref>/edge-config
GET /v1/projects/<ref>/edge-config/history
GET /v1/projects/<ref>/edge-config/tokens
POST /v1/projects/<ref>/edge-config/tokens
DELETE /v1/projects/<ref>/edge-config/tokens/<id>
PUT /v1/projects/<ref>/edge-config/tokens/<id>/connections/<target>
DELETE /v1/projects/<ref>/edge-config/tokens/<id>/connections/<target>
GET /v1/projects/<ref>/rolling-releases
PUT /v1/projects/<ref>/rolling-releases
POST /v1/projects/<ref>/rolling-releases/advance | promote | abort
GET /v1/projects/<ref>/kv?prefix=session:
GET /v1/projects/<ref>/kv/<key>
PUT /v1/projects/<ref>/kv/<key>
DELETE /v1/projects/<ref>/kv/<key>
GET /v1/projects/<ref>/ai-gateway
PUT /v1/projects/<ref>/ai-gateway
GET /v1/projects/<ref>/ai-gateway/logs
POST /v1/projects/<ref>/ai-gateway/providers
PATCH /v1/projects/<ref>/ai-gateway/providers/<id>
DELETE /v1/projects/<ref>/ai-gateway/providers/<id>
PUT /v1/projects/<ref>/ai-gateway/budgets
GET /v1/projects/<ref>/ai-gateway/keys
POST /v1/projects/<ref>/ai-gateway/keys
PATCH /v1/projects/<ref>/ai-gateway/keys/<id>
DELETE /v1/projects/<ref>/ai-gateway/keys/<id>
PUT /v1/projects/<ref>/ai-gateway/keys/<id>/limits
PUT /v1/projects/<ref>/ai-gateway/aliases
PUT /v1/projects/<ref>/ai-gateway/hooks
GET /v1/projects/<ref>/ai-gateway/spend?days=30
GET /v1/projects/<ref>/middleware
PUT /v1/projects/<ref>/middleware