sandbox_start

goal を持つ新しいボックスを起動します。リポジトリが公開するサービスをそれぞれの名前で宣言でき、環境、シークレット、サイズ、ツールチェーンも選べます。ボックスの id、URL、受け取ったサービスを返します。

入力 出力
name、goal、services[{name, port, targetPort, web, fromBox, fromPort, version, containerPort, env}]、externalBaseUrl、environment、secrets(名前の一覧)、size、toolchains、orientation、idleTimeoutMin、restore、waitForCapacitySec(goal は必須、ほかは任意) id、arch、sceneUrl、webUrl、takeoverUrl、status、orientation、orientationNote(横向きに切り替わったときだけ)、startedFrom(snapshot)、size、vcpus、memoryGb、受け取った services(web サービスには url 付き)、受け取った secrets、toolchains、idleTimeoutMin、toolchainsNote(toolchains に常に使える言語を書いたときだけ)、environment、アカウントに IP 許可リストがあるときの boxUrlAccess、next
  • name:このボックスの用途を、人が読める言葉で書きます("会員センターの返金フロー")。アプリはこれをボックスのタイトルとして表示します。指定しないと人にはボックス ID しか見えません。

  • goal(必須):このボックスがなぜあるのか、どうなれば完了かを、人が読む言語で 1〜2 文で書きます("会員センターの返金ボタンで返金が台帳に記録されるようにする。ブラウザで返金が最後まで通れば完了")。アプリはこれをボックスのカードとページに表示し、sandbox_status と sandbox_list も返すので、この会話が閉じられても別の会話が作業を引き継げます(別の会話が残したボックスを引き継ぐ)。最大 500 文字で、それより長いと 500 文字で切られます。指定しないか空白だけだと失敗します:goal is required: one or two sentences, in the language the person reads, on why this box exists and what done looks like. The person sees it on the box's card in their app, and whoever picks the box up if this conversation is closed reads it in sandbox_status. Call sandbox_start again with goal set. REST(POST /v1/boxes)では goal は任意です。

  • orientation:AI の画面とレビューのスクリーンショットを portrait(390 × 844)または landscape(1280 × 800)にします。省略するとアカウント設定を使い、初期値は縦向きです。この設定はボックスごとに保持され、後でアカウント設定を変えても既存のボックスは変わりません(動いているボックスは REST の PUT /v1/boxes/{id}/orientation で切り替えます)。画面でデスクトップ向けのレイアウトを見せるなら landscape にしてください。Chromium のウィンドウは最小 500 ピクセル幅で、縦向きの画面にちょうど収める引数は sandbox_exec にあります。縦向きを指定した(またはアカウント設定が縦向き)のにボックスのイメージが画面を回転できない場合、ボックスは代わりに横向きで起動し、結果の orientationNote がそう伝えます。ページは 1280 × 800 のウィンドウで描かれ、スマホサイズの画面にはイメージが更新されてから新しいボックスが必要です。

  • restore:アイドルの規則で止められ、凍結コピーがまだ残っているボックスの id です。コピーは停止から 24 時間残ります(sandbox_status の restorableUntil、または all: true を付けた sandbox_list)。新しいボックスを起動する代わりに、その同じボックスを凍結した時点のまま戻します。/work、動いていたプログラム、id、URL もそのままで、next もそう伝えます。goal はいつもどおり渡してください。ほかの設定はボックス自身のものが使われ、渡しても無視されます。つないでいたスマホは残らないので、新しく attach してください。凍結コピーが残るのはアイドルの規則で止まったボックスだけで、sandbox_stop で止めたものは戻せません(409 と、起動し直す方法が返ります)。戻すには新しいボックスと同じく、プランのボックス上限に空きとクレジットが必要です。その時点でどのホストにも空きがなければ、ボックスは凍結した状態で戻り、次にボックスに作用する呼び出しで起きます。

  • size:1(既定)、2、4、8 のいずれかの単位(1 単位 2 vCPU・8 GB メモリ・40 GB ディスク)。ボックスの生存期間中は固定で、クレジットは比例して消費します。ボックスはどれも x86_64(amd64)の Debian 12 で、結果にも arch: "amd64" と出ます。arm64 のイメージについては sandbox_exec を参照してください。1 は Web フロントエンドと、Node、Python、Go のたいていの作業に足ります。Android、Gradle、重い docker compose(1 つのボックスで複数のサービスをイメージとしてビルドするなど)には 2 以上を使います。toolchains:java-17、java-21、android、dotnet、rust、flutter(Web と Android。android も有効になります)、esp32(ESP32 向け PlatformIO。Arduino と ESP-IDF、コンパイルのみ)、python-3.12、python-3.13(その Python が PATH の先頭で python、python3、pip になります。選ばなければ python3 は Debian の 3.11 で、Pillow、numpy、boto3 が入っています)。すべてのコマンドで有効になります。Go、Node、Python 3.11、PHP と Composer、MySQL・Postgres・S3 のテスト用サーバー(psbx-testdb)、psql、redis-cli、mysql、ImageMagick、PowerShell 7、Azure CLI と GitHub CLI、clang、cmake は選ばなくても常に使えます。toolchains にこれらの言語(go、node、python)を書いても無視され、結果の toolchainsNote にその旨が出ます。

  • services:リポジトリがボックス上で公開するサービス。それぞれ name、呼び出し側が接続する port、任意の targetPort と web を持ちます。

    • ボックスが起動した時点から、各名前はボックス内に専用のアドレス(198.18.0.0/15 から割り当て)を持ち、ボックスの DNS はプロセスにもコンテナにもそのアドレスで名前を答えます。配線は不要です。name:port は、プロセスが待ち受ける targetPort(省略時は port)に届きます。呼び出し側は name:port に接続したまま、プロセスは targetPort で待ち受けます。既定のブリッジ、ユーザー定義の Docker ネットワーク、docker compose のコンテナも同じようにこれらの名前に届きます。ボックスの規則はボックス自身のプログラムにもすべてのコンテナネットワークにも適用され、ボックス内の Docker の DNS はボックスのリゾルバーに転送します。
    • プロセスは 0.0.0.0 で待ち受ける必要があります。127.0.0.1 だけで待ち受けるプロセスには、自分の URL(下の sceneUrl を参照)からは届きますが名前では届きません。コンテナは targetPort を公開してください(compose の ports:、docker run の -p <targetPort>:<コンテナ内のポート>)。
    • 同じ targetPort を共有する名前は 1 つのプロセスで、sandbox_wire では一緒に切り替わります。名前ごとにアドレスがあるので、port が同じで targetPort が違う名前は別のサービスです。api.acme.internal:8080 と billing.acme.internal:8080 は、8080 と 8081 で待ち受ける 2 つのプロセスにそれぞれ届きます。
    • ボックスのポート 80 と 9095 は boxd のもの(80 はシーンの入口、9095 はその API)で、どのプロセスも待ち受けられません。呼び出し側がポート 80 で接続するサービスは targetPort を宣言します。{ "name": "api.acme.internal", "port": 80, "targetPort": 8080 } なら、呼び出し側は api.acme.internal:80(または http://api.acme.internal/)に接続し、プロセスは 8080 で待ち受けます。targetPort がないと 400 で失敗します:port 80 in the box is boxd's scene entry; add targetPort, the port your process listens on — callers keep dialing <name>:80。ポート 9095 も同じで、targetPort 自体も 80 や 9095 にはできません。それ以外のポートは 443 などの特権ポートも含めて使えます(コマンドは root で動きます)。ポート 80 に targetPort が要るのはボックス内のサービスだけです。ポート 80 で登録した環境のアドレス(内部ロードバランサー)と、ポート 80 で宣言したリンク({ "name": "api.test.internal", "port": 80, "fromBox": "<A>", "fromPort": 8080 })はそのまま使えます。どの名前もボックス内に専用のアドレスを持ち、ボックス自身のポート 80 とぶつからないからです。80 で宣言も登録もしていない名前(別のポートで宣言したサービスやリンク、別のポートだけで登録した環境のホスト)にポート 80 で接続すると、ボックスのプログラムからもコンテナからも 502 nothing listens on <name>:80 in this box: that name is declared on another port. Dial the port it is declared on, or declare it on port 80 with targetPort, the port your process listens on になります。localhost:80 は今も boxd のシーンの入口で、sceneUrl と同じく最初に宣言したサービスに届きます。2026-09-24 20:30 UTC より前に起動したボックスでは、そういう名前に 80 で接続すると最初に宣言したサービスに届きます。誰も宣言していないほかのポートでは、接続は拒否されるか、ボックスでそのポートを待ち受けているものに届きます。名前のポート 9095 は boxd の API に届き、404 を返します。
    • 名前はホスト名(小文字の英数字、-、.)か、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、100.64.0.0/10 の内側のプライベート IPv4 アドレスまたは範囲(例: 172.16.0.0/16)です。ボックス内(コンテナも含む)からその範囲のそのポートへの接続は、ボックス内のそのサービスの targetPort に届きます(external モードでは externalBaseUrl への転送)。範囲は、ホスト名ではなく自分のレジストリから得た IP に直接つなぐ呼び出し側のためのもので、専用のアドレスは持たず、ここでしか宣言できません。
    • web: true は、人がブラウザで開いて製品をそのまま使える各サービスを示します。API、データベース、JSON しか見せないものには付けません。web サービスはそれぞれ専用の URL を持ち、アプリの「使う」ボタンはその最初のもの(下の webUrl)を開きます。
    • fromBox(と任意の fromPort)を付けると、この項目はこのボックス内のサービスではなく、自分の別のボックスへのリンクになります。このボックスの name:port がボックス fromBox の fromPort に届きます。別のボックスへのリンクを参照してください。
    • version(と任意の containerPort)を付けると、自分のプロセスの代わりに、公開したバージョンをその名前で動かします。ボックスがイメージを pull してバックグラウンドで起動します。公開したバージョンを動かすを参照してください。
  • externalBaseUrl:変更していない HTTP サービスがある場所。http(s)://host[/path] で、クエリや認証情報は含められません。例: ステージングのゲートウェイ。ボックスにこれがあると、宣言したすべてのサービスは external モードで始まります。プロセスからもコンテナからも、name:port への各 HTTP リクエストがパスはそのまま、Host を書き換えて externalBaseUrl に転送されます。ポート 80 で宣言したサービスも同じです。ボックス上で直接動くプログラムは localhost:<port> と 127.0.0.1:<port> でも転送先に届きます。ただし 80 と 9095(boxd のもの。localhost:80 はシーンの入口)と、box モードのサービスのプロセスが待ち受けているポートは除きます。ボックス自身の IP や 172.17.0.1 のそのポートからは届かないので、名前で接続してください。boxd はサービスのポートを占有しないので、自分のプロセスはいつでもその targetPort で起動でき、その後 sandbox_wire で名前をそちらに切り替えます。任意。

  • environment:自分の環境(sandbox_environments で一覧できます)。ボックスはそのプライベートアドレス(データベース、Redis、内部サービス。ネットワーク内のコネクタ経由)に接続でき、各サービスの設定を /work/.sbx/env/<サービス>.env と .sh として受け取り、externalBaseUrl を指定しなければ環境のものを使います(同じく external で始まります)。services に宣言したホスト名が環境のアドレスでもある場合(ポートは問いません)、その名前はボックスを指し、reachable から外れます。宣言した範囲に入り同じポートの環境の IP アドレスも同様です。環境の各ホスト名もボックス内に専用のアドレスを持つので、ポート 80 のアドレス(内部ロードバランサーなど)もほかと同じように使え、自分のプロセスが環境のアドレスと同じポート番号で待ち受けることもできます。結果の environment には、サービスごとの keysCount と notable(まず確かめるべき接続・モード・権限の設定。全部の keys は sandbox_environments と env ファイルにあります)、接続ごとの rttMs、同じ環境で稼働中の自分の他のボックス activeBoxes が入ります。アカウントに環境があるのに environment なしで起動すると、next がそれらの名前を挙げます。ボックスは後から環境につなげないからです。環境を参照してください。

  • secrets:sandbox_secrets の名前から、ボックスが割り当てられた時点で環境変数として注入するものを選びます。列挙しないものは注入されません。あとから sandbox_secrets で追加できます。

  • idleTimeoutMin:使用(ボックスに作用するツール呼び出しか復帰。ボックスの状態を参照)がこの分数ない場合に、凍結中かどうかにかかわらず、ボックスを完全に停止します(/work は消えます)。指定すると既定の期限の代わりになります。ボックスを起こしておくもの(background: true で起動したコマンドは最後に使われてから 1 時間まで、人の引き継ぎ、別のボックスからのリンク接続)があると、それが終わるまで停止は後回しになります。この時間は最後に使われた時刻から数えます。人がボックスの URL を使ってもリセットされません。その人のリクエストがボックスを起こしておくのは最後に使われてから 2 時間までで、そのあとボックスは凍結し、期限を過ぎているのですぐに完全に停止します。ページの読み込みで凍結したボックスが復帰すれば使用として数え、数え直しになります。0 または省略:凍結したボックスは最後に使われてから 24 時間で停止します。sandbox_status と sandbox_list の stopsAt は、それまでに使われなければこの規則がボックスを完全に止める時刻です。その 30 分前からは、そのボックスのツール結果の最後に毎回予告が付きます(ボックスの状態)。idleTimeoutMin の上限は 10080 分(7 日)です。大きい値は 10080 に制限され、結果に説明が付きます。凍結したボックスもその間はアカウントの同時ボックス数に数えられます。いずれの場合も、ボックスは 10 分アイドルで凍結されます(ボックスの状態を参照)。使われ続けているボックスに最長稼働時間はありません。

  • startedFrom:snapshot:ボックスは microVM のホスト上でスナップショットから復元され、数秒で使えます。どのボックスも microVM で動き(sandbox_status の placement: "microvm")、アイドルになると凍結されます。

  • sceneUrl:https://<id>-<key>.box.parallelsandbox.com。web の有無にかかわらず、最初に宣言したサービスを開きます。box モードならボックス内の 127.0.0.1:<targetPort>、external モードなら externalBaseUrl です。サービスを宣言していなければ 503 を返します。ボックスが止まるまで有効で、凍結しても変わりません。

  • services[].url:web: true で宣言したサービスはそれぞれ専用の URL を持ち、同じ仕組みでそのサービスを開きます。ボックスが止まるまで有効です。最初に宣言したサービスが web なら、その url は sceneUrl そのもの、https://<id>-<key>.box.parallelsandbox.com で、ほかの web サービスは https://<id>-<key>-<targetPort>.box.parallelsandbox.com です。web のないサービスには url がありません。sandbox_status と REST のボックス一覧(GET /v1/boxes)も同じ URL を返します。boxd がサービスごとの URL より古いボックス(それが出る前に起動したもの)は何も返さず、使えるのは sceneUrl だけです。

  • webUrl:web を付けた最初のサービスの url。なければ返りません。ボックスが止まるまで有効です。つまり最初に宣言したサービスが web なら sceneUrl、そうでなければ最初の web サービスの -<targetPort> の URL です。アプリの「使う」ボタンはこれを開くので、services の順番は関係ありません。sandbox_review のカードが開くのは、webUrl ではなく、その呼び出しが open で指定した入口です。

  • ボックスの外から届くのは、web を付けたサービスと、sceneUrl 経由の最初に宣言したサービスだけです。-<port> のポートが web のないサービスのものなら 404 で、サービスのないポートと同じです。ポートを順に試しても、隠れたサービスは見つかりません。

  • これらの URL にはランダムな key が入っているので、ボックス id から組み立てることはできません。sandbox_start または sandbox_status が返すものをそのまま使ってください。key が違う URL は、存在しないボックスと同じ 404 になります。

  • これらの URL の前にログインはありません。URL の key が唯一の保護です。URL を持つ人は誰でもそのサービスを使え、それを通じてそのサービスが届くものすべてにも届きます。環境につながったボックスのフロントエンドは、開いた人の代わりに dev のデータベースや内部サービスを呼びます。それを許してよい相手にだけ共有してください。チャットアプリは貼られた URL を取りに行ってプレビューを作ります。プレビューのサービスはページを受け取り、その取得はボックスの利用として数えられ、HTML を求める取得はページの読み込みと同じく凍結中のボックスを復帰させます。ボックスが止まると URL は使えなくなり(503 box is terminated)、凍結中は 503 を返します。

  • ボックスの URL はボックスごとに専用のホストを持つので、ID プロバイダーに前もって登録できません。ボックスでのサインイン(SSO、OAuth)には、チームがローカルで使うログイン方法でボックスのコピーを動かすか、ボックスが動いている間だけ、その正確な URL を dev クライアントの許可リダイレクト URI に加えます。https://*.box.parallelsandbox.com/* のようなワイルドカードは決して登録しないでください。すべてのアカウントのボックスがこのドメインを共有しています。ボックスの URL でのサインインを参照してください。

  • これらの URL を通ったリクエストは Host: 127.0.0.1:<targetPort> でプロセスに届き、公開ホストは X-Forwarded-Host、スキーム(https)は X-Forwarded-Proto、クライアントは X-Forwarded-For に入ります。相対パスのリダイレクトはそのまま動き、Host から絶対 URL や Cookie のドメインを組み立てるアプリは転送ヘッダーを信頼するよう設定してください(Express:app.set('trust proxy', true))。

  • これらの URL で開いたページは人のブラウザ、つまりボックスの外で動きます。そのコードが別オリジンの API を呼ぶ場合、リクエストがボックス内の API に届くのは、API のサービスに web があり(ページにはそのサービスの url を渡す必要があります)、API がページのオリジンに CORS で応じる場合だけです。より簡単なのは、フロントエンドの dev server に、ボックス内の API の名前へ転送する同一オリジンのプロキシを置く方法です。例:Vite の server: { proxy: { '/api': 'http://api.acme.internal:8080' } }。ブラウザはページ自身のオリジンの /api/... を呼び、ボックス内で動く dev server がそれを /api の接頭辞ごと api.acme.internal:8080 に転送します。接頭辞を残すか外すかは dev に合わせてください(チーム構成)。

  • next:次の手順のヒント(1 行)。

同時に動かせるボックス数は Free 3、Pro 5、Max 20 です。凍結中のボックスも数に入り、この上限はアカウント単位で、すべての接続で共有します。上限に達すると sandbox_start は 429 plan <plan> allows <n> boxes at once and <m> are running or frozen; frozen boxes still hold a slot, so list them with sandbox_list (GET /v1/boxes) and sandbox_stop one you no longer need. で失敗し、そのうち何台が凍結中か、誰も使わなければアイドルルールが最初に止めるのはどれで、いつかが続きます。どのボックスが枠を使っているかは sandbox_list でわかります。

新しいボックスを置けるホストがないときは、sandbox_start は 503 no box capacity right now: every host is full and more is being started. Try sandbox_start again in about 5 minutes; a box of size 4 or 8 can take a few minutes longer. No box was created by this call. で失敗します。すぐに別のホストが起動します。望まない限り size を小さくする必要はありません。ParallelSandbox がボックスのイメージを切り替えた直後の約 3 分間は、代わりに the hosts are switching to a new box image, which takes about 3 minutes と返ります。

  • waitForCapacitySec(0〜1800)は、すぐ失敗せずに呼び出しの中で空きを待ちます。全ホストが満杯、ホストがイメージを切り替え中、またはプランの同時ボックス数が上限のとき、sandbox_start は 15 秒ごとに再試行して進捗(No room for the box yet: … Waited 45s of 5m0s; trying again in 15s.)を送り、ボックスが起動した時点でそれを返します。それまではボックスはありません。待てる長さは、クライアントが 1 回の呼び出しで待てると宣言した時間が上限です。stdio アダプターはホストのツールタイムアウト(PSBX_TOOL_TIMEOUT_SEC、既定 60 秒で 25 秒待てます。上げ方は sandbox_review)を宣言し、何も宣言しない接続は最大 25 秒です。待っても空かなかったときは、どれだけ待ってなぜやめたかをエラーで伝えます。
  • 呼び出しの結果が届かなかったとき(接続が切れた、またはクライアントが呼び出しを取り消して「中断」と表示した)、stdio アダプターがボックスを探します。結果が戻らなかった場合は、この会話がその name と goal でさっき起動したボックスを返答で伝えるか、起動しなかったと伝えます。中断された後、同じ name と goal で次に sandbox_start を呼ぶと、2 台目を起動せずに中断された呼び出しが起動したボックスを返します(それでも別に起動したいときはもう一度呼びます)。ほかのツールの結果の末尾にもそのボックスを知らせる一文が付きます。

すべてのツールとトピックはツールリファレンスに並んでいます。