One Server

ドキュメント

エッジ: 分析、エッジ設定、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