bit / bithub 向けの独立 relay サーバー実装です。 MoonBit 生成物に依存せず、TypeScript
だけで動作します。
- local: Deno (
deno serve) - edge: Cloudflare Workers + Durable Object
GET /healthPOST /api/v1/publish?room=<room>&sender=<sender>&topic=notify&id=<id>&sig=<sig?>GET /api/v1/poll?room=<room>&after=<cursor>&limit=<n>POST /api/v1/review?room=<room>&sender=<sender>&pr_id=<id>&verdict=approve|denyGET /api/v1/review?room=<room>&pr_id=<id>GET /api/v1/inbox/pending?room=<room>&consumer=<consumer>&limit=<n>POST /api/v1/inbox/ack?room=<room>&consumer=<consumer>GET /ws?room=<room>GET /api/v1/key/info?sender=<sender>POST /api/v1/key/rotatePOST /api/v1/key/verify-github
GET /api/v1/cache/exchange/discoveryGET /api/v1/cache/exchange/pull?after=<cursor>&limit=<n>&peer=<node_id>&room=<room?>POST /api/v1/cache/exchange/pushGET /api/v1/cache/issues/pull?room=<room>&after=<cursor>&limit=<n>GET /api/v1/cache/issues/sync?room=<room>&after=<cursor>&limit=<n>
POST /api/v1/trigger/callbackGET /api/v1/trigger/results?room=<room>&after=<cursor>&limit=<n>POST /api/v1/github/webhookGET /api/v1/github/webhook/dlq?after=<cursor>&limit=<n>POST /api/v1/github/webhook/dlq/retry?delivery_id=<id>
POST /api/v1/admin/github/repos/registerPOST /api/v1/admin/github/repos/<id>/pushPOST /api/v1/admin/github/repos/<id>/actions/dispatch
NAT/FW 越しに git clone/fetch を relay 経由で中継するセッション機構。
POST /api/v1/serve/register→{ ok, session_id, session_token }GET /api/v1/serve/poll?session=<id>&session_token=<token>&timeout=<sec>POST /api/v1/serve/respond?session=<id>&session_token=<token>GET /api/v1/serve/info?session=<id>&session_token=<token>GET /git/<session_id>/<path>?session_token=<token>— git HTTP リクエスト
session_token は register 時に自動生成され、以降の全エンドポイントで必須です。 query param
session_token または header x-session-token で渡せます。 トークン不一致の場合は
403 invalid session token を返します。
フロー:
Clone side → GET /git/<session>/info/refs?session_token=xxx → (queued)
Serve side → GET /api/v1/serve/poll?session=...&session_token=xxx → requests[]
Serve side → POST /api/v1/serve/respond (with response) → ok
Clone side → (response resolved)
bit hub sync向けにpayload.kind=hub.recordをそのまま保持bithub互換として{"payload": {...}}のラップ形式も受理して展開- room validation:
[A-Za-z0-9][A-Za-z0-9._-]{0,63} - dedupe: room 内の同一
idはaccepted=false
構造化ログ(JSON line)を標準出力へ出します。
relay_event: incoming ref / issue sync / cache replication などのイベントログrelay_audit: API の監査ログrelay_metric: メトリクスログ
代表的なメトリクス:
relay.request.success_rate: 操作単位の成功率・平均レイテンシ・再試行回数relay.cache.persist.retry: cache 永続化の再試行回数
BIT_RELAY_AUTH_TOKEN を設定すると /api/v1/* と /ws で Bearer 認証を要求します。
export BIT_RELAY_AUTH_TOKEN='secret-token'publish はデフォルトで署名必須です(RELAY_REQUIRE_SIGNATURE=true)。
必要ヘッダ:
x-relay-public-key: ed25519 公開鍵(base64url)x-relay-signature: 署名(base64url)x-relay-timestamp: Unix epoch secondsx-relay-nonce: nonce(再利用不可)
署名対象文字列 (v1):
v1
sender=<sender>
room=<room>
id=<id>
topic=<topic>
ts=<timestamp>
nonce=<nonce>
payload_sha256=<sha256(canonical_json(payload))>
senderごとに公開鍵を TOFU で自動登録- 以後、同じ
senderで別公開鍵は409 sender key mismatch - 同じ nonce 再利用は
409 replayed nonce
POST /api/v1/key/rotate body:
{
"sender": "alice",
"new_public_key": "...base64url...",
"ts": 1771599000,
"nonce": "nonce-rotate-1",
"old_signature": "...",
"new_signature": "..."
}署名対象文字列 (v1):
v1
op=rotate
sender=<sender>
new_public_key=<new_public_key>
ts=<timestamp>
nonce=<nonce>
old_signature: 現在の鍵で署名new_signature: 新しい鍵で署名
deno task dev
# default: http://127.0.0.1:8788optional env:
HOST(default:127.0.0.1)PORT(default:8788)BIT_RELAY_AUTH_TOKENRELAY_MAX_MESSAGES_PER_ROOM(default:1000)PUBLISH_PAYLOAD_MAX_BYTES(default:65536)RELAY_PUBLISH_LIMIT_PER_WINDOW(default:30)RELAY_PUBLISH_WINDOW_MS(default:60000)RELAY_ROOM_TOKENS(JSON, e.g.{"secure":"token"})MAX_WS_SESSIONS(default:100)RELAY_REQUIRE_SIGNATURE(default:true)RELAY_MAX_CLOCK_SKEW_SEC(default:300)RELAY_NONCE_TTL_SEC(default:600)RELAY_MAX_NONCES_PER_SENDER(default:2048)RELAY_NODE_ID(cache exchange ノード ID)RELAY_PEERS(CSV: peer relay URLs)RELAY_PEERS_JSON(JSON: peer relay URLs,RELAY_PEERSより優先)RELAY_PEER_AUTH_TOKEN(relay 間共有 Bearer token。設定時は/api/v1/cache/exchange/*と/api/v1/cache/issues/*が必須認証)RELAY_PEER_SYNC_INTERVAL_SEC(default:30)RELAY_PEER_REPO_FF_COMMIT_WINDOW(default:30, peer 間 repo 判定で見る commit 数)RELAY_ISSUE_SOURCE_OF_TRUTH(last_write|github|bit, default:last_write)RELAY_REPO_ID(明示 repo ID, 例:bit-vcs/bit)RELAY_REPO_ORIGIN_URL(origin URL の明示上書き)RELAY_REPO_RECENT_COMMITS(CSV: 最近 commit hash)RELAY_CACHE_PROVIDER(memoryorr2, default:memory)RELAY_CACHE_TTL_SEC(default:86400)RELAY_CACHE_MAX_BYTES(任意、上限 bytes)RELAY_CACHE_R2_BUCKET(R2 利用時)RELAY_CACHE_R2_PREFIX(default:relay-cache/)RELAY_GITHUB_WEBHOOK_SECRETRELAY_TRIGGER_WEBHOOK_URLRELAY_TRIGGER_WEBHOOK_TOKENRELAY_TRIGGER_EVENT_TYPE(default:relay.incoming_ref)RELAY_TRIGGER_REF_PREFIXES(CSV, default:refs/relay/incoming/)RELAY_CONFIG_JSON(JSON override)
RELAY_REPO_ID を指定しない場合は git remote.origin.url と直近 commit を参照して、 peer cache
sync の対象 repo を自動推定します(deno task dev は git 実行権限付き)。
互換モード(従来の unsigned publish 許可):
export RELAY_REQUIRE_SIGNATURE=falsepnpm install
pnpm run dev:cf
pnpm run deploywrangler.jsonc は Durable Object RelayRoom を使用します。
sprites 側は 0.0.0.0:8080 を公開ポートとして扱うため、relay も PORT=8080 で常駐させます。
# sprite CLI でログイン済みの前提
# 既定: sprite=myapp, auth=public, signature-required=true
just deploy-sprites myapp直接実行する場合:
SPRITE_NAME=myapp URL_AUTH=public RELAY_REQUIRE_SIGNATURE=true \
tools/deploy-sprites.shログ確認:
just sprites-logs myappbit 側は hub sync issue-url で共有URLを発行できます:
bit hub sync issue-url relay+https://<sprite-url> --room <room> --room-token <token>exe.dev の VM に SSH でデプロイします。
# SSH 先を指定してデプロイ
just deploy-exe user@myapp.exe.dev
# ログ確認
just exe-logs user@myapp.exe.dev直接実行する場合:
EXE_HOST=user@myapp.exe.dev PORT=8080 tools/deploy-exe.shrelay を常駐させた状態で、外部からファイルの読み書きフローを検証できます。
# ローカルで検証
deno task dev &
just test-serve
# exe.dev 上の relay で検証
just test-serve https://myapp.exe.devテストスクリプト (tools/test-serve-flow.sh) は以下のフローを検証します:
POST /api/v1/serve/register— セッション登録GET /api/v1/serve/info— セッション状態確認- 読み取り (GET): clone 側がファイルを要求 → serve 側が poll → respond → clone 側が受信
- 書き込み (POST): clone 側がデータを送信 → serve 側が poll でボディを受信 → respond
┌──────────┐ ┌──────────────┐ ┌──────────┐
│Clone side│ │ bit-relay │ │Serve side│
│(外部) │ │ (exe.dev) │ │(ファイル │
│ │ │ │ │ 所有者) │
└────┬─────┘ └──────┬───────┘ └────┬─────┘
│ │ │
│ GET /git/ID/f │ │
│ ───────────────> │ (queue) │
│ │ <────────────── │
│ │ poll (待機) │
│ │ ──────────────> │
│ │ requests[] │
│ │ <────────────── │
│ │ respond(body) │
│ <─────────────── │ │
│ 200 (file body) │ │
just test
# 実機メッシュ検証(3ノードをローカル起動して伝搬確認)
just test-multi-relay-mesh 19081レポートは docs/performance-reports.md を参照。
# 全シナリオ
just bench https://bit-relay.mizchi.workers.dev
# 新規: multi-relay + cache hit/miss + issue sync
RELAY_URLS=https://relay-a.example,https://relay-b.example \
just bench-scenario multi-relay-cache-issue-sync https://relay-a.example