コンテンツにスキップ

クイックスタート

前提 —— capture-ledger のスタックが動いていること

Section titled “前提 —— capture-ledger のスタックが動いていること”

Windmill は capture-ledger のスタックの 2 つを使う。OpenFGA には、下の windmill にクロールを許可するで「windmill は acme のクロールを 始めてよい」という許可を書き込む。BrowserHive は、 クロールがページを撮りに呼ぶ。始める前に、capture-ledger の クイックスタートの §1〜§5 (DNS ドメイン、submodule と .env、stack:up、データベース、OpenFGA の 2 つの ID)を済ませ、 スタックが動いていることを確かめる。向こうの pnpm run dev:up は §3 以降をまとめて やるので、手で打つのは §1 と §2 だけでもよい。

§4 のスクリプトを忘れないこと。 BrowserHive は走らせるものの顔ぶれを持たないので、 scripts 表が空だと、クロールはページの中で何も走らせない —— スクロールも遅延読み込みも 起きないまま、成功したアーカイブが出る。下の doctor のスクリプトがそれを言う。

ターミナルウィンドウ
curl -s -o /dev/null -w '%{http_code}\n' -H 'authorization: Bearer dev-key' http://127.0.0.1:8090/stores
# 200 なら動いている。000 なら止まっている → cd ~/projects/crawler/capture-ledger && pnpm run stack:up

dev-key は開発用の OpenFGA の既定の鍵。止まったままだと、下の fga:grant が 「OpenFGA (http://localhost:8090) に届きません」で止まる。

以下のコードブロックは、どれも 1 行目の cd で「どの repo で打つか」を示す(2 つの repo は ~/projects/crawler/ に並べて clone してある前提。別の場所なら読み替える)。capture-scheduler の コマンドを capture-ledger で打つと、pnpm は Missing script としか言わない。

ターミナルウィンドウ
cd ~/projects/crawler/capture-scheduler
sudo container system dns create capture-scheduler # マシンごとに 1 度だけ
./setup.sh
container-compose up -d
pnpm install
pnpm run windmill:bootstrap # workspace と token を作り、2 枚のファイルに書く(下)
pnpm run windmill:push # script・flow・schedule を入れる

**貼る行はもう出ません。**bootstrap が書くのは、この repo の中の 2 枚だけです:

ファイル 中身
.env.local WINDMILL_TOKEN。この repo の script が読む
.dev/capture-ledger.env capture-ledger に渡す 4 行。読むのは向こう

capture-ledger の設定を書き換えるのは、向こうで打つコマンドの仕事です:

ターミナルウィンドウ
cd ~/projects/crawler/capture-ledger
pnpm run connect # 上の 4 行を読み、capture-ledger の .env.local に書く

渡るのはこの 4 行。どれが欠けても、クロールは最後まで走りません:

capture-scheduler/.dev/capture-ledger.env
CAPTURE_LEDGER_CRAWL_WEBHOOK_URL=http://127.0.0.1:8000/api/w/crawler/jobs/run/f/f/waggle/crawl_level
CAPTURE_LEDGER_CRAWL_WEBHOOK_TOKEN=<bootstrap が作った token>
CAPTURE_LEDGER_API_HOST=0.0.0.0
CAPTURE_LEDGER_OIDC_ISSUER=http://127.0.0.1:9099
行 なぜ要るか
…_CRAWL_WEBHOOK_URL・…_CRAWL_WEBHOOK_TOKEN クロールを Windmill に投げる先。無いと /api/crawls そのものが無い(404)
CAPTURE_LEDGER_API_HOST=0.0.0.0 段の報告はコンテナから来る。127.0.0.1 で待つ API には届かない
CAPTURE_LEDGER_OIDC_ISSUER flow は JWT で名乗る。無い API は開発用ヘッダの設定になり、段の報告は 401 になる

bootstrap は打つたびに新しい token を作ります(前に作ったものも、使えるまま残ります)。 打ち直したら connect も打ち直すこと —— 引き渡しファイルは新しい token で書き換わるので、 取りに行かないと capture-ledger は古い token のままです(古いほうも生きているので、 壊れるのではなく「直したはずなのに変わらない」形になります)。

connect を打ったら、capture-ledger 側で issuer を起こし、API を起こし直します。

capture-ledger のクイックスタート §6 で起こした API を、Ctrl-C で止めてから 同じコマンドをもう一度打ちます —— 新しく 2 本目を立てるのではありません。 API が設定を読むのは起動のときの 1 度だけなので、走ったままのプロセスは 上の 4 行を知りません。並べて立てようとしても、2 本目は EADDRINUSE: address already in use 0.0.0.0:7070 で落ちます。どこに残っているか 分からなくなったら、capture-ledger の pnpm run dev:status が pid と起動時刻を出し、 pnpm run dev:down が止めます(issuer も API もコンテナではないので、 container-compose down では消えません)。

oidc:issuer は API より先に起こします。API は起動時に issuer の鍵を取りに行くので、 居ないと JWT の設定で立ち上がれません。

ターミナルウィンドウ
cd ~/projects/crawler/capture-ledger
pnpm run oidc:issuer # 動かし続ける
pnpm run api # §6 で起こしたものを止めてから、同じ口でもう一度

4 行が効いたかは、API の起動ログの最後の行が言います:

Archive API listening on 0.0.0.0:7070 — identity: JWT (http://127.0.0.1:9099); crawl level reports: ready

blocked なら、その上の warn が足りない行を名指しします(capture-ledger v0.44.0 から)。

http://127.0.0.1:8000 で Windmill が開く。

Windmill の flow が capture-ledger を呼ぶためのトークンと、呼ぶ先を、Windmill の変数に入れる:

ターミナルウィンドウ
cd ~/projects/crawler/capture-scheduler
pnpm run windmill:capture-ledger-token # トークンと、コンテナから見た API の宛先と、ts-compile-service の宛先を入れる

API の宛先(u/admin/waggle_api_url)は、CAPTURE_LEDGER_API_URL を書かなければ default ネットワークの gateway から組みます(container network inspect default)。network を作り直すと 変わるので、そのときは windmill:capture-ledger-token をやり直します。issuer を起こし直したときも 同じ(トークンの経路)。

Windmill は capture-ledger を windmill という名前で呼ぶ(トークンの sub。組織は acme)。 capture-ledger は、クロールを始める・段を報告する・索引を渡す・締める、のたびに 「windmill は acme のクロールを始めてよいか」を OpenFGA に訊き、許可が無ければ 404 を返す。 手で始めたクロールでも、段の報告は windmill の名前で来る。

pnpm run fga:grant submitter windmill acme (あなたが 1 度だけ)
│ 書く
▼
OpenFGA user:windmill submitter organization:acme
▲
│ 訊く: user:windmill は organization:acme で can_submit か
│ (クロールを始める・段の報告・索引・締めのたび)
capture-ledger API ◄── Windmill(sub=windmill, orgs=[acme] のトークン)
│
└─ 行が在れば 202。無ければ 404 {"error":"not found"}

その許可を 1 行書き込む(capture-ledger 側で 1 度だけ。取り消すのは fga:revoke):

ターミナルウィンドウ
cd ~/projects/crawler/capture-ledger
pnpm run fga:grant submitter windmill acme
# windmill は acme のクロールを始められます (書いた: user:windmill submitter organization:acme)

CAPTURE_LEDGER_SUBJECT / _ORGANIZATIONS を変えたなら、その名前で。書けたかどうかは、下の doctor の can_submit の行が教える —— 足りなければ、capture-ledger が実際に見た 名前で、打つべきコマンドを出す。

ターミナルウィンドウ
cd ~/projects/crawler/capture-scheduler
pnpm run doctor # 14 本の点検。全部 ✓ なら、クロールが最後まで走る設定になっている
pnpm run smoke # https://example.com/ を 1 本撮り、WACZ を取り出せたら「撮れた」

doctor は、このページの見出しごとに「その手が済んでいるか」を、その手が作ったものに訊く。 立っているかだけでなく、script の中身が repo と同じか、Windmill が持っているトークンで クロールを始められるか、worker の中から API と BrowserHive に届くか、まで見る。✗ は、何を 直すかと、このページのどの節の手かを言う。✗ の点検に頼る点検は走らせず、「先に〇〇を」とだけ 出す —— 直すものが 1 つなら、✗ も 1 本。

立ち上げる
✓ windmill http://127.0.0.1:8000/api/version
…
✗ capture-ledger api http://127.0.0.1:7070/healthz
✗ oidc issuer http://127.0.0.1:9099/.well-known/openid-configuration
・crawl route 先に capture-ledger api を
・jwt 先に capture-ledger api・oidc issuer を
直すもの (2):
capture-ledger api —— capture-ledger の API が答えない → cd ../capture-ledger && pnpm run api
手順: https://uraitakahito.github.io/capture-scheduler/ja/quickstart/#立ち上げる
…

smoke は、doctor が全部 ✓ のときだけ 1 本撮る。本番と同じ道 —— capture-ledger の POST /api/crawls に flow と同じトークンで頼み、Windmill の flow に撮らせ、段の報告を待つ —— を通し、台帳に載った archive を署名付き URL で取り出して、先頭が PK(WACZ は zip)であることまで 見る:

点検 doctor の 14 本とも ✓
始める POST /api/crawls → 202 crawl 545e912c-… https://example.com/
待つ running
run http://127.0.0.1:8000/run/01a0ba19-…?workspace=crawler
終わり succeeded (max_depth) —— 撮ったページ 1・6 秒
取り出す archive 928d640f-… の先頭が PK (WACZ = zip)
撮れた https://example.com/

撮れなかったら、smoke が落ちた段と直し方を言う(原因 と 直す の 2 行)。言われた手を直して smoke をもう一度。自分が始めたクロールは、失敗しても時間切れでも Ctrl-C でも締めるので、次の 1 本が 409 で塞がれることはない。Windmill の画面で追うなら、smoke が出す run の URL を開く(見方は Windmill UI の「失敗を掘る」)。

ターミナルウィンドウ
cd ~/projects/crawler/capture-scheduler
pnpm run smoke https://example.org/ # 撮る URL を変える
pnpm run smoke --timeout 300 # 待つ上限(秒。既定 180)
pnpm run smoke --no-doctor # 点検を飛ばす(直した直後の撮り直し)
pnpm run smoke --close-running # 409 のとき、走っている 1 本を締めてから撮る

smoke は capture-ledger v0.43.0 以上を前提にする(最後の段の run と、取れなかったページの理由を 台帳が返すのは、その版から)。

どれも実物で起こして確かめた見え方(2026-09-19)。

見えたもの 意味 直す
doctor のスクリプトが ✗ / smoke が 400 no scripts are registered 台帳にスクリプトが無いか、全部が既定に入らない(無効)。何も名指さないクロールは断られる capture-ledger で pnpm run scripts import .upstream/capture-scripts。全部が無効なら pnpm run scripts enable <id>
doctor の windmill が ✗ で、下の点検が「先に windmill を」 Windmill が落ちている container-compose up -d(この repo で)
doctor の push が ✗「repo と中身が違う」 script を直したが push していない(または Windmill の UI で直した) pnpm run windmill:push(違いを見るだけなら windmill:diff)
doctor の worker→browserhive が ✗「名前が引けない」(止めた直後の数秒は「3 秒で答えない」)/smoke の原因が [cap] BrowserHive に届きません: … BrowserHive のコンテナが止まっている(止めたコンテナは名前ごと消える) capture-ledger で pnpm run stack:up
smoke が succeeded・撮ったページ 0 で、原因が … net::ERR_NAME_NOT_RESOLVED … 撮る URL の名前が引けない URL の綴り。正しければ、BrowserHive のコンテナから外の名前を引けるか
doctor の can_submit が ✗「… のクロールの許可が無い」/smoke の原因が … /pages → 404 windmill にクロールの許可が無い capture-ledger で pnpm run fga:grant submitter windmill acme
doctor の can_submit が ✗「トークンが通らない」/smoke の原因が … /pages → 401 issuer を起こし直し、Windmill のトークンが古い pnpm run windmill:capture-ledger-token
doctor の jwt が ✗「ヘッダで名乗る設定で動いている」(起動ログの最後の行は crawl level reports: blocked) capture-ledger の API が JWT を受けない(CAPTURE_LEDGER_OIDC_ISSUER が効いていない) capture-ledger で pnpm run connect を打ち、API を起こし直す
doctor の container→api が ✗「API が 127.0.0.1 で待っている」/smoke の原因が Unable to connect 段の報告がコンテナから API に届かない(CAPTURE_LEDGER_API_HOST=0.0.0.0 が効いていない) capture-ledger で pnpm run connect を打ち、API を起こし直す
smoke が 409「走行中のクロールがある: …」 前の 1 本が running のまま 終わるのを待つか pnpm run smoke --close-running
host │ コンテナ
dev issuer 127.0.0.1:9099 │
│ POST /token │
▼ │
windmill:capture-ledger-token ───────────────┼──► secret 変数 u/admin/waggle_token
│ │
capture-api 0.0.0.0:7070 ◄───────────┼── trigger_crawl.ts

dev issuer は loopback から出さないこと。 あれは頼まれれば誰の名前でもトークンを 出すので、コンテナから引ける場所に置いた瞬間、ブリッジに届く誰もが windmill を 名乗れる —— fga:grant で windmill だけに与えたクロールの許可を誰でも使えることになり、 JWT にした意味が消える。

鍵を作る力は host に残し、跨がせるのは出来上がったトークン 1 本だけ。

issuer を再起動したら pnpm run windmill:capture-ledger-token をやり直すこと。 issuer は起動のたびに鍵をメモリ上で作り直すので(意図された挙動)、古いトークンは 401 になる。report_level.ts と trigger_crawl.ts の失敗メッセージがそう書いてあるのは、 踏みやすく、かつ status だけからは辿れないため。

API は起こし直さなくてよい。鍵の名前(kid)も一緒に替わるので、API は新しいトークンを 1 本見た時点で鍵を取り直す(capture-ledger v0.42.1 から。それより前は kid が固定で、 API が古い鍵を最長 10 分覚えたまま、新しいトークンを 401 にしていた)。issuer を起こし直した 直後の 30 秒は、新しいトークンも通らないことがある —— doctor の jwt がそう言う。

picker で見る(トークンを貼る)

Section titled “picker で見る(トークンを貼る)”

CAPTURE_LEDGER_OIDC_ISSUER を立てた capture-ledger は JWT だけを受け付ける。ブラウザで開く picker(http://127.0.0.1:7070/)もそれに合わせて、名乗りの 2 欄の代わりにトークンの欄を出す (capture-ledger v0.45.0 から)。API の設定は変えずに、トークンを貼って読む:

ターミナルウィンドウ
cd ~/projects/crawler/capture-ledger
pnpm run --silent oidc:token --subject "$(whoami)" --org acme | pbcopy
open http://127.0.0.1:7070/

「トークン」の欄に貼って「読み込む」を押す。一覧の上に「(名前)(acme)として見ている」と出れば、 API はそのトークンを受けている。トークンは 1 時間で切れる。issuer を起こし直したときも、 API が新しい鍵を取り直した時点から、前のトークンは通らない —— 「401 — このトークンは通らない…」と 出たら、同じコマンドで取り直して貼る。 画面の使い方は capture-ledger のアーカイブを見る。

JWT が dev ヘッダより優先されるのは capture-ledger 側の意図された設計で、「両方設定された 環境で弱いほうへ落ちない」ため。picker のために CAPTURE_LEDGER_OIDC_ISSUER を外さないこと —— 外した API は flow の段の報告を 401 で断り、走っていたクロールは running のまま残って、 以後の起動を全部 409 で塞ぐ。戻し方は capture-ledger のクイックスタートの 「409 が続くとき」。