コンテンツにスキップ
コンソール →
公式サイト →
URL を貼り付けて AI に質問 https://keelson.dev/ja/llms.txt

まとめて読む

はじめに


ドキュメントガイド

Keelson は、AI ツールなどで作った社内アプリを、チームで使えるようにするサービスです。アプリをデプロイして動かすだけでなく、利用者にログインを求めたり、アプリを使えるメンバーを指定したりできます。

利用を始めるには、Keelson コンソールでアカウントを作成するか、ログインしてください。

お使いの ChatGPT や Claude Code などでも、Keelson について質問できます。ページ上部の「URL を貼り付けて AI に質問」にある「コピー」ボタンで URL をコピーし、質問と一緒に送ってください。アプリの構成を伝えれば、デプロイできるかどうかや、必要な変更点についても相談できます。

次の例をコピーして試せます。アプリの説明は、ご自身の構成に置き換えてください。

https://keelson.dev/ja/llms.txt
この URL のドキュメントを読み、必要に応じてリンク先も参照してください。
Python と SQLite で作った社内アプリを Keelson にデプロイしたいです。
対応しているかと、必要な準備を教えてください。

Python / Node.js / Go の Web アプリ、ビルド済みの静的サイト、決まった時刻や間隔で処理を行う定期実行ジョブに対応しています。社内の数人〜数百人が使う業務アプリを想定しており、利用者数は料金に影響しません。

常時起動や WebSocket を前提とするアプリ、GPU が必要なアプリなどには対応していません。対応するフレームワークや詳しい制約は、対応アプリと制約を確認してください。

対応条件を満たす既存のアプリも、実行環境に合わせて設定やコードの一部を変更することでデプロイできます。アプリ全体を Keelson 向けに書き直す必要はありません。既存の外部データベースやサービスも引き続き利用できます。必要な準備は、他のツールで作ったアプリを持ち込むを参照してください。

Keelson 以外の環境でアプリを動かす際の注意点は、FAQにまとめています。

サービス概要と料金は公式サイトにあります。

アプリを開発・デプロイする方

Section titled “アプリを開発・デプロイする方”

まず読む

必要になったら参照する

メンバーやアクセス権を管理する方

Section titled “メンバーやアクセス権を管理する方”

AI エージェントを使って開発・デプロイする方

Section titled “AI エージェントを使って開発・デプロイする方”

アプリの対応条件やデプロイに必要な設定は、Keelson Deploy Spec にまとめています。AI エージェントに次の URL を渡し、「この仕様とアプリのコードを確認して、Keelson にデプロイするために必要な変更を提案してください」と依頼してください。

https://keelson.dev/ja/docs/reference/deploy-spec.txt

CLI の keelson install-agent コマンドでインストールできる AI エージェント用スキルには、Deploy Spec の内容に加え、Python / Node.js / Go ごとのコードの変更手順が含まれています。

ドキュメントの全文をまとめて AI に渡したい場合は、/ja/llms-full.txt を利用できます。各ページを Markdown で取得するには、URL の末尾の / を .md に置き換えてください(例:/ja/docs/introduction/quickstart/ → /ja/docs/introduction/quickstart.md)。

導入前のよくある質問と回答は、FAQ にまとめています。

クイックスタート

サンプルアプリを Keelson にデプロイし、ログインした人だけが使えることを確認します。

  • 所要時間: 約 15 分。ビルドの待ち時間を含みます。
  • 必要なもの: Keelson のアカウントと、コマンドを実行するターミナル。サンプルアプリ用に外部サービスの API キーを用意する必要はありません。
  • 完了の目安: ログイン後は挨拶の画面が表示され、ログインしていないシークレットウィンドウではログイン画面が表示されます。

このガイドでは、Claude Code / Codex / Cursor のいずれかを使ってデプロイします。AI エージェントを使わず、CLI から直接デプロイすることもできます。その場合は手順 3 を省き、手順 4 の CLI コマンドを実行してください。

Keelson のアカウントがまだない方は、Keelson コンソールでアカウントを作成してください。

1. CLI をインストールしてログインする

Section titled “1. CLI をインストールしてログインする”

Keelson CLI は、ターミナルからログインやデプロイを行うためのツールです。使っている OS のタブを選び、次のコマンドでインストールしてください。

Terminal window
curl -fsSL https://keelson.dev/install.sh | sh
keelson コマンドが見つからない場合

インストーラーは、コマンドを探す場所(PATH)を変更しません。次の行を ~/.zshrc や ~/.bashrc に追加し、ターミナルを開き直してください。

Terminal window
export PATH="$HOME/.keelson/bin:$PATH"

続けてログインします。ブラウザが開くので、Keelson のアカウントでログインしてください。

Terminal window
keelson login

2. サンプルアプリをダウンロードする

Section titled “2. サンプルアプリをダウンロードする”
  1. サンプルアプリをダウンロード(ZIP)します。
  2. ZIP を展開します。
  3. 展開した keelson-hello フォルダを Claude Code / Codex / Cursor で開きます。AI エージェントを使わない場合は、このフォルダでターミナルを開いてください。
サンプルの中身を見る
main.py
from html import escape
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
def index(request: Request):
email = escape(request.headers.get("X-Keelson-User-Email") or "ゲスト")
return f"""
<!doctype html>
<html lang="ja"><body style="font-family: sans-serif; padding: 2rem">
<h1>こんにちは、{email} さん</h1>
<p>このアプリは Keelson で動いています。</p>
</body></html>
"""
requirements.txt
fastapi
uvicorn
keelson.yaml
slug: hello
runtime: python-slim
command: "uvicorn main:app --host 0.0.0.0 --port $PORT"
db:
mode: none

このアプリは、Keelson が付けるヘッダーからログイン中のユーザーのメールアドレスを読んで表示します。認証のコードは 1 行もありません。

3. AI エージェント用スキルをインストールする

Section titled “3. AI エージェント用スキルをインストールする”

スキルは、AI エージェントが参照する Keelson の設定・デプロイ手順です。Keelson CLI に同梱されています。展開した keelson-hello フォルダでターミナルを開き、使っているエージェントのタブを選んで、次のコマンドを実行してください。

Terminal window
keelson install-agent claude-code --global

--global を付けると、同じユーザーのほかのプロジェクトでも使えるようになります。付けない場合は、現在のプロジェクトにだけインストールします。

公開リポジトリからスキルをインストールする場合

インストール方法は、keelson install-agent、Claude Code プラグイン、npx skills add のどれか 1 つだけを使ってください。方法を組み合わせると、同じスキルが 2 つ入ったり、互いに上書きしたりすることがあります。

Claude Code では、マーケットプレイスを追加してからプラグインをインストールします。

/plugin marketplace add keelsonhq/skills
/plugin install keelson@keelson-skills

ほかのエージェントでは、次を実行します。

Terminal window
npx skills add keelsonhq/skills

これらの方法でインストールされるのはスキルだけです。手順 1 の CLI のインストールとログインも必要です。Claude Code プラグインとして入れたスキルは keelson login では自動更新されず、Claude Code のプラグイン更新で追随します。

配置先や更新方法は CLI コマンドを参照してください。

エージェントのチャットに次のように依頼します。

このアプリを Keelson にデプロイして

エージェントがデプロイを開始します。初回の完了には数分かかります。デプロイの開始時には、デプロイ ID と、次のようなアプリの URL が発行されます。

https://<ワークスペース>--hello.keelson.run

AI エージェントを使わない場合は、keelson-hello フォルダで次のコマンドを実行します。

Terminal window
keelson deploy --new

URL はデプロイの完了前にも発行されます。URL が表示された時点では、まだビルドや起動の途中の場合があります。URL を開く前に、次のコマンドで状態を確認してください。<deploy-id> は、デプロイ時に表示された ID に置き換えます。

Terminal window
keelson status <deploy-id>

たとえば ID が d1 なら、keelson status d1 を実行します。status: completed と表示されれば、デプロイの処理は完了しています。続けて手順 5 でアプリが実際に開けることを確認してください。

queued、building_image、starting などの場合は処理中です。少し待ってから、同じコマンドでもう一度確認してください。failed や rolled_back の場合は成功していません。表示された理由と次に必要な操作を確認してください。詳しくはトラブルシューティングを参照してください。

5. アプリの表示とログインを確認する

Section titled “5. アプリの表示とログインを確認する”
  1. 発行された URL をブラウザで開きます。ログイン画面が表示された場合は、Keelson にログインしてください。「こんにちは、… さん」と本文が表示されることを確認します。
  2. Keelson にログインしていないシークレットウィンドウで同じ URL を開きます。アプリではなく、Keelson のログイン画面が表示されることを確認します。

「ゲスト」と表示される場合も、未ログインという意味ではありません。このサンプルでは、ログイン方法によってメールアドレスを取得できない場合に「ゲスト」と表示します。

これで、ログインした人だけが使えるアプリを専用 URL で開けるようになりました。デプロイしたアプリは Keelson コンソールにも表示されます。

  • 同僚に共有する — 同僚を招待し、相手のアカウントでアプリを開けることを確認します。手順はチームにアプリを共有するを参照してください。
  • 表示を変えて更新する — main.py の文言を変えて、もう一度「デプロイして」と頼みます。更新内容を反映する方法と、以前のバージョンに戻す方法は更新と復元を参照してください。
  • 自分のアプリをデプロイする — 対応アプリと制約で対応状況を確認し、持ち込みガイドで準備を進めます。
  • 仕組みを理解する — 基本コンセプトで、ワークスペースやアクセス権、Keelson とアプリの役割分担を確認します。

基本コンセプト

Keelson では、社内アプリとメンバーをワークスペースにまとめて管理します。アプリを増やすたびに、ログインやメンバー管理の仕組みを作り直す必要はありません。

このページでは、基本的な用語と、Keelson が用意する機能・アプリ側で実装する機能の違いを説明します。

用語意味
ワークスペースメンバーやアプリをまとめて管理する単位
アプリデプロイして利用する業務ツール。案件管理や日報など
メンバーワークスペースに参加している人
グループ部署やチームなど、アクセス権をまとめて設定するためのメンバーの集まり
デプロイアプリのコードと設定を送り、Keelson 上で使える状態にすること

たとえば、同じワークスペースに「営業日報」と「経費申請」を作り、営業チームには営業日報、全社員には経費申請を共有できます。

Web アプリや静的サイトをデプロイすると、HTTPS で保護された専用 URL が発行されます。証明書の取得や更新は Keelson が行います。

https://<ワークスペース>--<アプリ>.keelson.run

通常のアプリ画面は、URL を知っているだけでは開けません。Keelson がログインとアプリの閲覧権限を確認し、許可されたアクセスだけをアプリに渡します。

ブラウザ
↓
Keelson:ログイン・アプリの閲覧権限・IP 制御の条件を確認
↓
アプリ:画面を表示し、業務の処理を実行

アプリ側でログイン画面や Keelson のログインセッションを実装する必要はありません。誰がアクセスしているかは、Keelson が渡すユーザー情報で確認できます。

外部サービスからの Webhook など、ブラウザのログインを使わない呼び出しには別の設定を行います。詳しくは外部システムとの連携を参照してください。

メンバーとアクセス権をまとめて管理する

Section titled “メンバーとアクセス権をまとめて管理する”

同じワークスペースのアプリでログインを共有する

Section titled “同じワークスペースのアプリでログインを共有する”

ユーザーは 1 回ログインすれば、同じワークスペース内で閲覧権限のあるアプリを開けます。Keelson のログインをアプリごとにやり直す必要はありません。アプリが独自の外部認証も使っている場合は、そのログインが別途必要になることがあります。

グループにアプリごとの権限を設定する

Section titled “グループにアプリごとの権限を設定する”

メンバーとグループはワークスペースで管理し、アプリごとにグループへ権限を割り当てます。

権限できること
閲覧(view)アプリを開いて利用する
管理(manage)アプリをデプロイし、設定やシークレットを変更する

「閲覧」は、アプリにアクセスして利用するための権限です。アプリ内でデータを登録・編集できるかどうかは、アプリ側の実装で決まります。

新しいアプリには、初期設定で次の権限が割り当てられます。

グループ含まれるメンバーアプリの権限
everyoneワークスペースの全メンバー閲覧
developersOwner / Admin / Developer ロールのメンバー管理(閲覧も含む)

特定のチームだけが利用できるようにする場合は、チームにアプリを共有するを参照してください。

管理権限は、業務上の承認権限とは別です。 「上司だけが申請を承認できる」「担当者だけが案件を編集できる」といったルールは、ユーザーやグループの情報を使ってアプリ側で実装します。

データの保存には、Keelson が管理するアプリ専用のデータベース「Managed SQLite」と、ファイルや画像・動画を保存・取得するためのライブラリ「Files / Media SDK」を利用できます。これらを含め、Keelson が提供する機能とアプリ側で実装する処理は、次のように分かれます。

機能Keelson が用意することアプリで実装すること
画面と業務処理アプリの URL と実行環境入力画面、集計、申請などの機能
ログインと権限ログイン、メンバー管理、アプリを開ける人の確認本人のデータだけ表示するなど、業務に応じた制御
データの保存Managed SQLite、Files / Media SDK保存先に接続し、データを読み書きする処理
API キーなどの秘密情報シークレットの保管とアプリへの受け渡し受け取った API キーなどを使って外部サービスに接続する処理
定期実行設定した時刻・間隔で処理を起動1 回分の処理を行って終了するコード
運用ログ、アプリ一覧、Managed SQLite のバックアップと復元業務上の処理結果やエラーをログに記録すること

通常は、AI エージェントに作りたい機能を伝え、アプリのコードと Keelson の設定を作成してもらいます。

API キーなどをシークレットとして管理する

Section titled “API キーなどをシークレットとして管理する”

共通の API キーはワークスペースに設定し、アプリへ環境変数として渡せます。アプリごとに別の値を設定して上書きすることもできます。秘密の値をコードや keelson.yaml に直接書く必要はありません。

詳しくは環境変数とシークレットを参照してください。

アプリ専用のデータベースを用意できる

Section titled “アプリ専用のデータベースを用意できる”

keelson.yaml に次のように設定すると、アプリ専用の Managed SQLite が用意されます。

db:
mode: libsql

接続情報は Keelson が渡します。接続してデータを読み書きするコードは、アプリ側で実装します。

Managed SQLite にはバックアップと復元が付きます。Files / Media のファイルは、このバックアップの対象外です。保存先の選び方はデータベースとファイルとメディアを参照してください。

決まった時刻・間隔で処理を実行できる

Section titled “決まった時刻・間隔で処理を実行できる”

毎朝の集計や定期的なデータ同期は、keelson.yaml の crons に設定します。Web アプリへのアクセスがなくても実行できます。設定方法は定期実行ジョブを参照してください。

アプリの一覧から運用状況を確認できる

Section titled “アプリの一覧から運用状況を確認できる”

コンソールでは、各アプリを誰が作ったか、誰が使えるか、最後にいつ利用・デプロイされたかを確認できます。作成者以外も、アプリの管理状況を把握できます。

起動とデータ保存について知っておくこと

Section titled “起動とデータ保存について知っておくこと”
  • アクセスがない状態が続くと、Web アプリは停止します。 次のアクセスで起動するため、停止後の最初の応答には通常より時間がかかることがあります。アクセス元に応答を返したあとも続ける処理は、完了まで実行される保証がありません。
  • アプリが動くサーバー内のファイルは、一時的な保存先です。 再起動や再デプロイで失われるため、残したいデータは Managed SQLite や Files / Media SDK を使って保存してください。

実装時の注意点はアプリが動くタイミングと対応アプリと制約を参照してください。

FAQ

対応アプリ、料金、アクセス権、データについて、導入前によくある質問をまとめました。設定や操作の詳細は、各回答のリンク先を参照してください。

Lovable / Bolt / v0 で作ったアプリはデプロイできますか

Section titled “Lovable / Bolt / v0 で作ったアプリはデプロイできますか”

構成によります。まずは、アプリを作った AI ツールや、アプリのコードを確認できる AI エージェントに、次の文章をコピーして質問するとスムーズです。

https://keelson.dev/ja/llms.txt
この URL のドキュメントを読み、必要に応じてリンク先も参照してください。
このアプリを Keelson にデプロイできるか確認してください。
コードや設定、利用しているデータベース・認証・外部サービスを確認し、
必要な変更と準備を教えてください。
判断に必要な情報が足りなければ、私に質問してください。

ビルド済みの HTML・CSS・JavaScript は静的サイトとして、Next.js などサーバー側の処理があるものは Web アプリとしてデプロイできます。静的サイトは手元でビルドし、外部サービスの接続先なども設定しておきます。

外部から接続できる DB やストレージは使い続けられますが、作成ツール内のデータや認証機能が自動で移るわけではありません。詳しくは持ち込みガイドを参照してください。

Keelson にデプロイしたアプリは、ほかの環境でも動きますか

Section titled “Keelson にデプロイしたアプリは、ほかの環境でも動きますか”

はい。移行先の環境に合わせて設定や必要なコードを変更すれば、ローカル環境や別のホスティングサービス、自分のサーバーでも動かせます。

動かす環境に合わせて、Python / Node.js などの実行環境や依存パッケージを用意し、起動設定やデータベースの接続先などを設定します。

Keelson に任せていたログイン・アクセス制御・定期実行が必要な場合は、移行先で用意します。Keelson が渡すログイン中のユーザー情報を使う処理は、移行先の認証方法に合わせて変更します。Files / Media SDK を使う処理も、移行先の保存サービスに合わせたコードの変更やファイルの移行が必要です。

現在は動きません。Streamlit は WebSocket が必須で、Keelson の実行モデルと合わないためです。Gradio は検証中です。対応アプリと制約を参照してください。

外部 DB(PostgreSQL など)に接続する構成であれば使えます。Managed SQLite(libSQL)には Django ORM 用のバックエンドがないため、Managed SQLite と組み合わせる構成は未対応です。db.mode: none にして接続情報をシークレットで渡し、gunicorn で起動します。フレームワーク別の注意点を参照してください。

アプリはずっと起動していますか

Section titled “アプリはずっと起動していますか”

いいえ。Web アプリはアクセスがない状態が続くと停止し、次のアクセスで起動します。そのため、停止後の最初の応答には通常より時間がかかることがあります。

決まった時刻の処理は定期実行ジョブに設定します。アクセス元に応答を返したあとも続ける処理は、完了まで実行される保証がありません。詳しくはアプリが動くタイミングを参照してください。

初めて利用する場合は、14 日間の無料トライアルが終了して、有料契約に移行した日からです。

  • Starter / Plus:カード登録なしで試せます。トライアル終了までにカードを登録しなければ、料金は発生しません。登録済みの場合は、終了時に有料契約へ移行して初回の請求が発生します。
  • Team:トライアル開始時にカード登録が必要です。トライアル期間中に解約すれば料金は発生せず、継続する場合は終了時に初回の請求が発生します。

有料契約に移行せずトライアルが終了すると、新しいデプロイ・アプリの起動・定期実行ができなくなります。データの扱いは、このページのトライアル終了・解約後の説明を参照してください。

トライアルを使い切ったアカウントで別のワークスペースを作る場合は、決済完了時に課金が始まります。詳しくは料金と契約の運用を参照してください。

作る人が 2 人、使う人が 50 人なら、どのプランですか

Section titled “作る人が 2 人、使う人が 50 人なら、どのプランですか”

Plus 以上です。Developers は、アプリを開発・管理するメンバーの人数枠です。開発・管理する人が 2 人なら、2 人分の枠が必要です。アプリを利用する 50 人は App User として追加でき、その人数は料金に影響しません。

Developers の人数枠には、Owner / Admin / Developer ロールのメンバーを数えます。あわせて「同時に使えるアプリ数」(Starter 1、Plus 2、Team 4)が用途に合うかを確認してください。詳しくはプランと制限を参照してください。

アプリを 10 個作って、普段は一部だけ使う場合、何が上限になりますか

Section titled “アプリを 10 個作って、普段は一部だけ使う場合、何が上限になりますか”

有料契約中は、ワークスペースに保持できるアプリ数に上限はありません。アプリ数について制限されるのは、「同時に使えるアプリ数」です。

直近 5 分にアクセスがあったアプリが、同時利用の枠を使います。アクセスがなくなったアプリは停止状態(スリープ)になり、枠が空きます。

トライアル中は、ワークスペースに保持できるアプリ数にも 3 個の上限があります。ただし、静的サイトとしてデプロイ済みのアプリは、この 3 個に数えません。詳しくはプランと制限を参照してください。

同時に使えるアプリ数の上限に達すると、利用者には何が表示されますか

Section titled “同時に使えるアプリ数の上限に達すると、利用者には何が表示されますか”

同時に使えるアプリ数の上限に達していると、停止中のアプリを開いた利用者には、起動できる枠が空くまで待つよう案内する画面が表示されます。枠を使っているアプリへのアクセスが約 5 分途絶えると、その枠が空きます。他のアプリが使われ続けている間は空きません。重要なアプリには優先起動(有料契約中は Plus 以上、トライアル中は全プラン)で枠を予約できます。

アプリ内で使う OpenAI などの API 利用料は、Keelson の料金に含まれますか

Section titled “アプリ内で使う OpenAI などの API 利用料は、Keelson の料金に含まれますか”

含まれません。外部 API の利用料は、その API の提供元との契約に基づいて別途発生します。API キーはシークレットとして設定し、アプリから利用します。詳しくは外部システムとの連携を参照してください。

URL を知っている人は誰でも開けますか

Section titled “URL を知っている人は誰でも開けますか”

開けません。ワークスペースのメンバーがログインし、そのアプリの閲覧権限を持っている場合だけ表示されます。ログインしていない人にはログイン画面が出ます。社外の人に見せるには、その人をメンバーとして招待します(App User は人数無制限)。詳しくは認証とログインを参照してください。

アプリとデータベースはどこで動きますか。データベースのバックアップはありますか

Section titled “アプリとデータベースはどこで動きますか。データベースのバックアップはありますか”

アプリとその Managed SQLite は、アプリ作成時に選んだリージョンに配置されます。既定は日本です。

※ 現在選べる配置先は日本のみです。米国西海岸は準備中で、提供開始前に指定すると作成が拒否されます。

Managed SQLite には、全プランでバックアップと復元が付きます。Files / Media SDK のファイルや外部 DB は、このバックアップの対象外です。

日次バックアップ、手動バックアップ、指定した時点のデータに戻す機能(PITR)、ダウンロードが使えます。詳しくはデータベースを参照してください。

アプリを更新すると、保存したデータは消えますか

Section titled “アプリを更新すると、保存したデータは消えますか”

Managed SQLite、Files SDK、Media SDK に保存したデータは、通常の再デプロイだけでは消えません。一方、アプリが動くサーバー内のローカルファイルは、再起動・再デプロイで失われます。

DB の変更処理やアプリのコードでデータを削除・更新した場合は、その変更が反映されます。保存先の違いはデータベース、変更を戻す方法は更新と復元を参照してください。

トライアル終了・解約後、アプリとデータはどうなりますか

Section titled “トライアル終了・解約後、アプリとデータはどうなりますか”

有料契約に移行せずトライアルが終了した場合や、解約して契約期間が終了した場合は、新しいデプロイ・アプリの起動・定期実行ができなくなります。アプリとデータがその場で削除されることはありません。

削除対象のアプリには、Owner / Admin 向けにコンソールとメールで削除が予告されます。猶予期間は、削除予告が記録された時点から、一度も支払いがないワークスペースでは 3 日、支払い実績があるワークスペースでは 30 日です。具体的な削除予定日は予告で確認してください。

猶予期間内に有料契約を開始・再開すれば、削除予告は取り消され、アプリとデータを保持できます。再契約せず猶予期間を過ぎると、対象のアプリは自動削除されます。削除後は、データベースのバックアップや Files のファイルなどが通常の利用から切り離された状態で 7 日間保管されますが、復元は保証されません。

Managed SQLite のバックアップは猶予期間中もダウンロードできます。Files / Media のファイルは契約終了までに取り出してください。詳しくは料金と契約の運用を参照してください。

プランをダウングレードすると、どうなりますか

Section titled “プランをダウングレードすると、どうなりますか”

有料契約中のダウングレードは、現在の請求期間の終わりに適用されます。それまでは現在のプランを利用できます。トライアル中のプラン変更は、直ちに適用されます。

プラン変更だけでアプリやデータが削除されることはありません。ただし、変更先のプランの上限を超えている場合は、利用に制限がかかります。たとえば、同時に使えるアプリ数が上限に達している間は、停止中のアプリを新たに起動できません。Developers の人数枠を超えている間は、開発・管理するメンバーを追加できません。

ほかの上限を超えた場合の扱いは、料金と契約の運用を参照してください。

アプリを作る


対応アプリと制約

Keelson は、Python・Node.js・Go の Web アプリ、静的サイト、定期実行ジョブに対応しています。 フレームワークやデータの保存方法によっては、コードや設定の変更が必要です。以下の表で対応状況を確認してください。

Dockerfile は不要です。アプリに含まれていても、Keelson のビルドには使用されません。

アプリ対応状況補足
Python の Web アプリ(FastAPI、Flask など)対応環境変数 PORT で指定されたポートで起動。Flask は gunicorn を使用
Node.js の Web アプリ(Express、Hono、Next.js など)対応環境変数 PORT で指定されたポートで起動。Next.js は next start
Go の Web アプリ(net/http、Gin など)対応Keelson がビルドして ./app で起動
ビルド済みの静的サイト / SPA(React、Vue、Svelte など)対応assets にビルド出力を指定
静的な画面 + API サーバー(ハイブリッド構成)対応assets.api で API のパスを指定
定期実行ジョブのみ対応共通の必須項目に加えて crons を設定し、Web サーバー用の command は省略
外部の PostgreSQL / MySQL / libSQL に接続する対応db.mode: none。接続情報はシークレットで渡す
外部 API(OpenAI、Slack、Google Sheets など)を呼ぶ対応外部通信は制限なし(SMTP ポート TCP 25・465・587・2525 のみ常に遮断)
ファイル SQLite に永続データを保存している(sqlite3、better-sqlite3 など)変更が必要接続部分を libSQL クライアントに置き換えて db.mode: libsql。SQL は通常そのまま(Managed SQLite は SQLite 互換)だが、クライアント API の差(Node.js は非同期化、Python は row_factory 等)に応じた修正が要る。Python / Node.js のローカル開発は file:local.db で従来どおり
ローカルファイルに残しておきたいデータを保存している変更が必要Managed SQLite か Files SDK / Media SDK へ。実行中に保存したローカルデータは再起動後に引き継がれない
アプリ内でタイマーやバックグラウンドスレッドを動かしている変更が必要時刻で動く処理は定期実行ジョブに移し、リクエストの後に続く処理はバックグラウンドタスクに投入する(アプリが動くタイミング)
Django外部 DB 接続のみ対応db.mode: none で PostgreSQL などの外部 DB に接続し、gunicorn で起動する。Managed SQLite(libSQL)には Django ORM 用のバックエンドがないため、Managed SQLite との組み合わせは未対応(フレームワーク別の注意点)
Streamlit未対応WebSocket が必須のため、現在は動きません(フレームワーク別の注意点)
Gradio検証中4 以上を検証中。現時点では動作を保証しません
Ruby / PHP / Java / Rust / .NET / Elixir未対応対応ランタイムは Python / Node.js / Go のみ
Dockerfile で apt パッケージを追加している未対応root 権限なし。画像・動画系は -media ランタイムで代替できる場合あり
常駐プロセス・デーモン・WebSocket サーバー前提未対応アイドル時に停止する実行モデルと合わない
Redis / Kafka / DB サーバーを自前で起動する未対応外部サービスとして接続するのは可
GPU が必要未対応

「変更が必要」のアプリは、コードや設定を変更して対応できる場合があります。AI エージェントに確認を依頼すると、必要な変更を提案します。

再生成できる一時データは、ローカルファイルに保存できます。一時的な SQLite の利用には db.local_sqlite の宣言が必要です。条件は keelson.yaml リファレンスを参照してください。

他のツールで作ったアプリ(Lovable、Bolt、v0 など)をデプロイする手順は持ち込みガイドにあります。

keelson.yaml の runtime で選びます。迷ったら -slim から始め、画像・動画・PDF 処理のライブラリ(Pillow、sharp、ffmpeg など)が必要になったら -media に切り替えます。

ランタイム言語バージョンビルド時の処理
python-slim / python-mediaPython 3.12requirements.txt または pyproject.toml から自動インストール
node-slim / node-mediaNode.js 24package.json と package-lock.json から依存を自動インストール。npm run build があれば実行
go-slim / go-mediaGo 1.26go.mod から go build し、./app として起動

Go のバージョンは、ビルドに使用するツールチェーンのバージョンです。表のビルド処理は、サーバー側の処理があるアプリや定期実行ジョブが対象です。依存パッケージはビルド時にインストールされます。command には起動コマンドだけを書きます。

  • Node.js: Keelson 上でのビルドには npm を使います。pnpm-lock.yaml / yarn.lock のみの構成は対応していないため、package-lock.json を用意してください。静的サイトは、手元で pnpm や Yarn を使ってビルドしたものも配信できます。
  • Go: CGO_ENABLED=0 でビルドします。cgo に依存するライブラリは使用できません。

依存ファイルの要件やビルドの制約は、keelson.yaml リファレンスを参照してください。

項目内容
OS / CPULinux / x86_64
権限・システムパッケージ非 root で動作します。sudo や apt-get は使えません
Web サーバーの起動0.0.0.0 と環境変数 PORT で指定されたポートで待ち受けます
データの保存ローカルファイルは永続保存されません。残したいデータには Managed SQLite や Files / Media SDK を使います
処理の実行リクエスト処理、バックグラウンドタスク、定期実行ジョブに対応します。詳しくはアプリが動くタイミングを参照してください

実行時間やアプリ数などの上限はプランと制限を参照してください。

Python(FastAPI)の例です。keelson.yaml と requirements.txt と app.py の 3 ファイルでデプロイできます。

keelson.yaml
slug: my-app
runtime: python-slim
command: "python app.py"
db:
mode: none
requirements.txt
fastapi
uvicorn
app.py
import os
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello from Keelson"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=int(os.environ["PORT"]))

通常は AI エージェントが keelson.yaml を書くので、自分で作る必要はありません。

アプリが動くタイミング

このページでは、サーバー側の処理があるアプリの実行方法を説明します。静的サイトの配信には、アプリサーバーの起動は必要ありません。静的な画面と API サーバーを組み合わせるハイブリッド構成では、以下の起動・停止の説明は API サーバーに当てはまります。

Keelson は、アクセスに応じてアプリサーバーを起動し、アクセスがない状態が続くと停止します。次のアクセスがあると、再び起動します。

画面表示や API の処理はリクエストを受けて実行し、リクエストの後に続ける処理はバックグラウンドタスクに投入し、毎日の集計などは Keelson の定期実行ジョブで実行します。アプリが常に起動していることを前提にした処理には対応していません。

アプリが停止しているときにリクエストを受けると、Keelson がアプリを起動します。このため、停止後の最初のアクセスでは、通常より応答に時間がかかることがあります。

再起動すると、メモリ上の変数や、アプリの実行中にローカルファイルへ保存したデータは引き継がれません。残しておきたいデータは、Managed SQLite や Files / Media SDK に保存してください。

詳しくはデータベース(Managed SQLite)とファイルとメディアを参照してください。

リクエストに応じて実行する処理

Section titled “リクエストに応じて実行する処理”

データの保存や外部 API の呼び出しなど、そのリクエストで必要な処理は、レスポンスを返すまでに完了させてください。

レスポンスを返した後にバックグラウンドで続ける処理は、最後まで実行されることが保証されません。FastAPI の BackgroundTasks や、完了を待たずに開始する非同期処理を使う場合は、処理方法の変更が必要です。短い処理はレスポンスを返す前に await で完了させ、レスポンスの後も続ける必要がある処理はバックグラウンドタスクに投入します。

AI の回答生成など、結果を少しずつ返す処理にはストリーミングを利用できます。ただし、接続が切れた後も処理を続ける仕組みとしては利用できません。

外部 API の重い呼び出し、PDF や画像の生成、リクエストの時間上限を超える処理などは、keelson.yaml の tasks に宣言し、アプリから SDK で投入します。Keelson は Web アプリとは別の実行環境でタスクのコマンドを 1 回起動し、失敗したときは自動で再試行します。

同じタスクが 2 回以上実行されることがあるため、2 回実行されても壊れないように書いてください。設定方法はバックグラウンドタスクを参照してください。

決まった時刻・間隔で実行する処理

Section titled “決まった時刻・間隔で実行する処理”

毎朝のレポート作成や定期的なデータ同期は、keelson.yaml の crons に設定します。Web アプリにアクセスがなくても、設定したスケジュールに従って実行されます。

ジョブは Web アプリとは別の実行環境で起動し、指定したコマンドの処理が終わると終了します。Web アプリと共有するデータは、データベースなどに保存してください。

アプリ内のタイマーやスケジューラで時刻を待つ方法は、アプリの停止中には動作しません。定期的な処理は Keelson の定期実行ジョブに設定してください。

設定方法は定期実行ジョブを参照してください。

やりたいこと実装方法
フォームの内容を保存するリクエスト処理中に保存し、結果を返す
AI の回答を順次表示するリクエスト処理中にストリーミングで返す
毎朝レポートを作成する定期実行ジョブに設定する
外部サービスと定期的に同期する1 回分の同期処理を定期実行ジョブで実行する
重い処理を受け付けて、レスポンスの後に実行するバックグラウンドタスクに投入する
受け付けた処理を後でまとめて実行する(待ち時間を許す)未処理の内容を DB に保存し、定期実行ジョブで処理する

DB に保存して後で処理する方法では、次のジョブ実行まで待ち時間が発生します。すぐに開始したい処理にはバックグラウンドタスクを使ってください。どちらの方法でも、処理の重複や失敗に備える必要があります。詳しくはバックグラウンドタスクと定期実行ジョブの設計を参照してください。

Keelson は常駐ワーカーを提供していません。リクエストの後に続ける処理は、バックグラウンドタスクに投入してください。

ここまでの説明は、サーバー側の処理が対象です。ブラウザ側のタイマーや画面の自動更新に、この制約は適用されません。

HTTP リクエスト、バックグラウンドタスク、定期実行ジョブには、それぞれ実行時間の上限があります。長い処理は、上限内に完了する単位に分けてください。

詳細はプランと制限、バックグラウンドタスク、定期実行ジョブを参照してください。

他のツールで作ったアプリを持ち込む

Lovable、Bolt、v0 などで作ったアプリや、手元で開発したアプリも、ソースコードを用意して Keelson に持ち込めます。対応状況とデプロイに必要な変更は、アプリの言語や構成、データの保存方法によって異なります。

まず AI エージェントにコードを確認してもらい、必要な変更を確認してからデプロイします。外部サービスにあるデータや利用者のアカウントは、コードをデプロイしただけでは Keelson に移りません。

アプリの作成に使ったツールからコードをエクスポートするか、Git リポジトリを手元に取得し、アプリのフォルダを AI エージェントで開きます。

Keelson CLI とエージェント用スキルのインストール、Keelson へのログインがまだの場合は、クイックスタートの手順 1 と 3 を参照してください。プロジェクト単位でスキルをインストールする場合は、サンプルアプリではなく、持ち込むアプリのフォルダで実行します。

次のように依頼してください。

このアプリを Keelson にデプロイしたいです。https://keelson.dev/ja/llms.txt を入口に、リンク先の対応アプリ・制約と Keelson Deploy Spec を読んでください。そのうえでコードを確認し、デプロイできるかと必要な変更を説明してください。データベース、ログイン、ファイルの保存先、定期的な処理を確認し、既存のデータや利用者のアクセス権に影響する変更は、実施前に説明してください。

エージェントが確認するのは、主に次の点です。

確認すること確認する理由
アプリの言語とフレームワークKeelson の対応範囲と、起動・ビルド方法を確認するため
データの保存先外部 DB を使い続けるか、Managed SQLite に変更するかを判断するため
ログインとアクセス権既存の利用者が自分のデータを引き続き使えるようにするため
ファイルの保存先再起動で消える保存方法になっていないかを確認するため
タイマーやバックグラウンド処理時刻で動く処理は定期実行ジョブ、リクエストの後に続く処理はバックグラウンドタスクのどちらに移すかを決めるため

対応している言語・フレームワークは対応アプリと制約、処理の実行方法はアプリが動くタイミングにまとめています。

3. データとログインの扱いを確認する

Section titled “3. データとログインの扱いを確認する”

外部のデータベースやストレージを使っている場合

Section titled “外部のデータベースやストレージを使っている場合”

アプリから接続できる外部サービスは、デプロイ後も使い続けられます。この場合、データを移す必要はありません。たとえば、外部 DB を使うアプリは db.mode: none にし、接続情報を設定します。

サーバー側で使う API キーやパスワードはシークレットで渡します。ブラウザ側で使うサービスの URL や公開用のキーは、そのサービスの案内に従って設定してください。秘密の値をブラウザに送るコードへ埋め込まないでください。

Supabase などをブラウザから直接呼ぶ構成では、外部サービス側のアクセス権設定も引き続き必要です。Keelson のログインでアプリを保護しても、外部サービスのデータ権限が自動で置き換わるわけではありません。

すでにログイン機能がある場合

Section titled “すでにログイン機能がある場合”

Keelson では、アプリを開くときに Keelson のログインが必要です。元のログイン機能を残すと、利用者は両方にログインすることになります。

元のログインを外せるかどうかは、ユーザーの識別やデータのアクセス権に使っているかで判断します。

元のログインの役割対応方法
アプリを開く人を制限するためだけに使っているKeelson のログインとアプリのアクセス権に置き換えることを検討できます
ユーザーごとに見せるデータを制限している元の認証を残すか、Keelson のユーザー情報を使って権限を判定するように変更します
データに元のサービスのユーザー ID を保存している既存のユーザーと Keelson のユーザーをどう対応付けるかを決めます

たとえば Supabase の RLS(行ごとのアクセス制御)で auth.uid() を使っている場合、ログイン機能だけを外すと、誰のデータかを判定できなくなります。ログイン画面を削除する前に、データへのアクセス方法を確認してください。

アプリ内のファイルに保存している場合

Section titled “アプリ内のファイルに保存している場合”

アプリの実行中にローカルファイルへ保存したデータは、再起動・再デプロイ後に引き継がれません。残したいものに合わせて保存方法を変更します。

残したいものKeelson の保存先
顧客情報、案件、申請など、検索・更新するデータManaged SQLite
アプリ内部の設定ファイルや処理済みデータの記録Files SDK
画面に表示する画像、利用者が開く PDF や添付ファイルMedia SDK

保存先を変更する場合は、コードの変更に加えて、既存データを移す手順もエージェントに確認してください。

デプロイ前に、Keelson CLI とエージェント用スキルのインストール、Keelson へのログインを済ませてください。まだの場合は、クイックスタートの手順 1 と 3 を参照してください。

変更内容を確認したら、エージェントに依頼します。

確認した内容で必要な変更と keelson.yaml の作成を行い、このアプリを Keelson にデプロイしてください。

エージェントはアプリの構成に合わせて設定を作成します。主な構成は次のとおりです。設定を自分で書く必要はありません。

アプリの構成デプロイ方法
静的サイト / SPA(ビルド済みの HTML・CSS・JavaScript)ビルドしたファイルを assets に指定して配信します。外部サービスの API をブラウザから呼ぶアプリも含みます
サーバー側の処理がある Web アプリ(Next.js、Express、FastAPI など)command で Web サーバーを起動します
静的な画面と API サーバーを組み合わせたアプリassets と command を設定し、assets.api で API のパスを指定します(ハイブリッド構成)

設定の読み方は keelson.yaml の設定、操作手順はデプロイするを参照してください。

静的サイトとハイブリッド構成のビルドについて

Section titled “静的サイトとハイブリッド構成のビルドについて”

静的サイトは、手元でビルドしてからデプロイします。Keelson にアップロードされるのは assets.dir の中身と keelson.yaml で、Keelson 上で npm install や npm run build は実行されません。

ハイブリッド構成でも、画面のファイルは手元でビルドし、assets.dir に用意してからデプロイします。サーバー側のビルドは Keelson が行いますが、assets として配信するのはアップロードしたファイルです。

ビルド時に読み込むサービスの URL や公開用のキーは、ビルド前に設定します。デプロイ後にサーバーの環境変数を設定しても、ビルド済みの画面には反映されません。エージェントには、設定を含めてビルドとデプロイを依頼してください。

  • Keelson の URL で画面が開き、必要な操作ができる
  • 既存のデータが表示され、追加・更新したデータも保存される
  • 画像や添付ファイルを表示・閲覧できる
  • 元の認証を残した場合も含め、想定したログイン手順で利用できる
  • 他のメンバーも利用でき、見せるべきでないデータは表示されない

コードと一緒には移らないもの

Section titled “コードと一緒には移らないもの”

作成ツールが提供するデータベース、認証、ファイル保存、サーバー側の関数は、ソースコードだけをデプロイしても移りません。外部から利用できるものは接続を維持し、利用できないものはデータの移行や処理の書き換えが必要です。

作成ツール内の編集機能も Keelson には移りません。デプロイ後のコード変更は、手元の AI エージェントなどで行い、再デプロイして反映します。

keelson.yaml の設定

keelson.yaml は、アプリの起動方法やデータベースの利用、定期実行などを Keelson に伝える設定ファイルです。デプロイするアプリのルートフォルダ(プロジェクトルート)に置きます。

通常は、デプロイを依頼した AI エージェントが作成・更新します。 このページでは、エージェントが作った設定を確認するときに役立つ項目を説明します。すべての設定項目や細かい条件は keelson.yaml リファレンスを参照してください。

Python の Web アプリで、Keelson のデータベースを使う例です。

slug: my-app # アプリの識別子
runtime: python-slim # Python の実行環境
command: "python app.py" # Web サーバーを起動するコマンド
db:
mode: libsql # Keelson の Managed SQLite を使う
項目確認すること
slugアプリを識別する名前です。https://<ワークスペース>--<slug>.keelson.run の一部になります
runtimeアプリの言語に合う実行環境です。Python / Node.js / Go から選びます
commandアプリの Web サーバーを起動するコマンドです。依存パッケージのインストールはビルド時に行われます
db.modeKeelson のデータベースを使うなら libsql、使わない場合や外部 DB を使う場合は none です

db.mode: libsql はデータベースを用意する設定です。アプリのコードにも、用意された DB に接続して読み書きする処理が必要です。既存のファイル SQLite が、この設定だけで Managed SQLite に切り替わるわけではありません。

Files / Media SDK だけを使うアプリは db.mode: none で構いません。Files / Media の利用を宣言する設定は不要です。

どのアプリにも slug、runtime、db.mode が必要です。そのほかの項目は、アプリの構成で変わります。

アプリの構成主な設定
Web アプリcommand に起動コマンドを書く
静的サイト / SPAtype: web と assets を設定し、command は書かない
静的な画面 + API サーバー(ハイブリッド)assets と command の両方を設定する
定期実行だけのアプリcrons を設定し、Web サーバー用の command は書かない

サーバー側の処理がある Web アプリには、crons を追加できます。その場合、type: web は指定しません。type: web と crons は併用できません。定期実行の設定は定期実行ジョブを参照してください。

ビルド済みのファイルを配信する例です。assets.dir にはビルド出力のフォルダを指定します。

slug: my-site
type: web
runtime: node-slim
db:
mode: none
assets:
dir: dist
fallback: index.html # SPA で、画面の URL を直接開いたときにも index.html を返す

静的サイトは手元でビルドしてからデプロイします。Keelson 上で依存パッケージのインストールやビルドは行われません。通常の静的サイトで、見つからないページを index.html に戻す必要がなければ fallback は省略できます。

/api 以下へのリクエストをサーバーで処理し、それ以外ではビルド済みの画面を配信する例です。

この構成でも、画面のファイルは手元でビルドし、assets.dir に用意してからデプロイします。サーバー側のビルドは Keelson が行いますが、assets として配信するのはアップロードしたファイルです。

slug: my-app
runtime: node-slim
command: "node server.js"
db:
mode: libsql
assets:
dir: dist
fallback: index.html
api: /api
やりたいこと設定詳しくは
アプリの説明を付けるdescriptionリファレンス
アプリに設定値を渡すenv環境変数とシークレット
API キーなどを使うsecrets に必要な名前を宣言する。秘密の値は書かない環境変数とシークレット
デプロイ時に DB のテーブルや列を変更するdb.migrateデータベース(Managed SQLite)
決まった時刻・間隔で処理を実行するcrons定期実行ジョブ
起動確認に使うページを指定するhealth.pathリファレンス
外部システムからアプリの API を呼ぶauth.endpoints にパスとメソッドを設定し、別途アプリトークンを発行する外部システムとの連携
  • アプリ名と保存先が意図どおりか。 slug が対象アプリの名前か、db.mode が利用するデータベースに合っているかを確認します。
  • API キーやパスワードが直接書かれていないか。 secrets には名前と説明だけを書き、値はコンソールなどから設定します。
  • 必要な定期処理が設定されているか。 アプリ内のタイマーに任せず、crons のスケジュールを確認します。

起動コマンドや依存ファイルの確認は、エージェントに依頼できます。次のコマンドでは、アップロードせずに設定と送信対象のファイルを確認できます。

Terminal window
keelson deploy --check --json

この確認だけでは、実際にアプリが起動するかやプランの上限内かまでは分かりません。デプロイ後はアプリの URL を開き、動作を確認してください。

keelson.yaml の変更は、次のデプロイで反映されます。エージェントに「設定を変更して再デプロイしてください」と依頼してください。

PORT などの環境変数は Keelson が設定します。古い設定を引き継ぐ場合は、廃止された項目を削除・置換してください。必要な変更や検証エラーの確認には、keelson.yaml リファレンスを参照してください。

Keelson SDK

Keelson SDK は、アプリのコードから Keelson のファイル保存・ユーザー情報・バックグラウンドタスク・メール送信などの機能を使うためのライブラリです。Python・Node.js(JavaScript / TypeScript)・Go 向けに提供しており、必要な機能だけを使えます。

アプリをデプロイするだけなら SDK の導入は不要です。たとえば「アップロードされた画像を残したい」「ログイン中のユーザーに合わせて画面を変えたい」ときに、アプリへ組み込みます。AI エージェントに実装を依頼する場合も、使っている言語と実現したいことを伝え、このページや各機能の解説を渡せます。

機能できること・用途使い方
Files SDKアプリ内部で使う非公開ファイルの保存・読み取り・更新・削除・一覧取得。設定 JSON や処理済みデータの記録などファイルとメディア:Files
Media SDK画像・PDF・添付ファイルの保存と、アプリを閲覧できるメンバー向けの URL 配信。商品写真の表示や申請書の閲覧などファイルとメディア:Media
Identity SDKログイン中のユーザー情報、所属グループ、ワークスペースのメンバー情報などの取得。作成者の記録やグループに応じた処理の分岐などログイン中のユーザー情報を使う
Tasks SDKkeelson.yaml の tasks に宣言したコマンドを、別のインスタンスで実行するよう投入し、状態を確認する。PDF の生成や外部 API の重い呼び出しなど、リクエストの後に続ける処理バックグラウンドタスク
Email SDKアプリの利用者へのメール送信と、配信結果(届いた・バウンス・苦情)の受け取り。パスワード再設定や申請の受付通知など外部システムとの連携:メールを送る

検索や同時更新が必要な顧客・案件などのデータには、データベース(Managed SQLite)を使います。接続には各言語の libSQL クライアントを使い、Keelson SDK とは別に導入します。

アプリの言語に合わせて、依存パッケージを追加します。基本的な機能は3言語で共通ですが、関数名や引数、補助機能は言語によって異なります。

すべての機能を keelson-sdk にまとめています。

Terminal window
pip install keelson-sdk

コードでは、使う機能を from keelson import files のように読み込みます。files のほか、media、identity、tasks、email を使えます。利用する依存管理ツールに合わせて、requirements.txt や pyproject.toml にも依存関係を記録してください。

機能ごとにパッケージが分かれています。必要なものだけをインストールします。

機能インストール
Filesnpm install @keelsonhq/files
Medianpm install @keelsonhq/media
Identitynpm install @keelsonhq/identity
Tasksnpm install @keelsonhq/tasks
Emailnpm install @keelsonhq/email

コードでは import * as files from "@keelsonhq/files" のように読み込みます。

1つのモジュールに各機能のパッケージをまとめています。

Terminal window
go get github.com/keelsonhq/go-sdk

コードでは github.com/keelsonhq/go-sdk/files のように、使うパッケージを import します。files のほか、media、identity、tasks、email があり、メンバーやグループの一覧取得には directory を使います。

SDK はアプリのサーバー側で使います。ブラウザで動く JavaScript に組み込むものではありません。静的サイト・SPA にはサーバーがないため SDK は使えません。利用したい場合は、SDK を呼ぶ API サーバーを足してハイブリッド構成にします。

接続先や認証情報は、利用する機能に応じて Keelson が環境変数で渡します。通常は SDK が読み取るため、コードにトークンや保存先を書き込む必要はありません。

  • Files / Media:keelson.yaml への追加設定は不要です。
  • Identity:ヘッダーから基本的なユーザー情報を読むだけなら追加設定は不要です。所属グループやメンバー情報の取得には、Directory API の有効化と再デプロイが必要です。
  • Email:keelson.yaml への追加設定は不要です。差出人は既定で <アプリの slug>@mail.keelson.run です。Team プランでは自社のドメインからも送れます。
  • Tasks:投入するタスクを keelson.yaml の tasks に宣言してデプロイします。詳しくはバックグラウンドタスクを参照してください。

Files / Media は、Keelson の接続設定がない手元の環境ではローカルのフォルダに保存します。Identity は、keelson dev serve で起動すると本番と同じユーザー情報のヘッダーを受け取り、利用者を切り替えられます(CLI なしなら KEELSON_LOCAL_MODE=1 でテスト用のユーザー情報を返します)。Tasks は、CLI の keelson dev task run を使って手元でコマンドを同期実行します(手元で動かす)。Email は手元の環境では送信できず、Keelson の接続設定がないため送信時にエラーになります。ローカルで保存したファイルやテスト用のユーザー情報が、デプロイ先へ自動で引き継がれるわけではありません。

Files には言語と OS による制約があります。保存先や制約はファイルとメディアのローカル開発、ユーザー情報のテスト方法はローカルで動かすを参照してください。SDK が使う設定値は環境変数リファレンスにまとめています。

データベース(Managed SQLite)

顧客情報、案件、申請内容など、アプリで登録・検索・更新するデータの保存先として Managed SQLite を利用できます。Keelson がアプリ専用に用意するデータベースで、保存したデータはアプリを再起動・再デプロイしても残ります。すでに利用している外部 DB に接続することもできます。

画像や設定ファイルにも、再起動後に残せる保存先があります。保存したいものと用途で選びます。

保存したいもの・用途保存先
顧客情報、案件、申請などを登録・検索・更新するManaged SQLite
設定ファイルや処理済みデータの記録を、アプリ内部で読み書きするFiles SDK
画像を画面に表示する、PDF や添付ファイルを利用者が開くMedia SDK

たとえば案件管理アプリなら、案件名や担当者は DB に、添付 PDF は Media に保存します。DB には添付ファイルの ID も記録し、案件とファイルを関連付けます。

ファイルの保存方法はファイルとメディアを参照してください。

ここでいう「ローカルファイル」は、Keelson 上でアプリが動くサーバー内に書き込むファイルです。アプリを再起動・再デプロイしたり、アクセスがなくなって停止したりすると、そのファイルは失われます。この性質を「揮発性」と呼びます。

./data.db や /tmp/data.db のように保存先のフォルダを変えても、再起動後にデータを残すことはできません(/data は Keelson が用意せず、アプリからも作成できません)。定期実行ジョブは Web アプリとは別の環境で動くため、これらのファイルを共有することもできません。

アップロードの受け取りや変換途中のファイルなど、処理中だけ使う一時ファイルは /tmp に置いて構いません。後で必要になるデータは、上の表の保存先に保存してください。

ファイル SQLite を使っているアプリ

Section titled “ファイル SQLite を使っているアプリ”

Managed SQLite は SQLite 互換の DB なので、SQLite で作ったアプリの SQL やテーブル定義は通常そのまま使えます。変えるのは接続まわりで、sqlite3 や better-sqlite3 でサーバー内のファイルに直接保存している箇所を、libSQL クライアントでの接続に置き換えます。クライアントの API には差があり、Node.js は同期の better-sqlite3 から非同期の @libsql/client になるため await を各ハンドラに足します。Python の libsql は row_factory・型アダプタ・例外クラスが sqlite3 と異なります。Python / Node.js では同じクライアントに file:local.db のようなファイルを渡せば、ローカル開発ではファイル SQLite として動きます。

sqlite3 や better-sqlite3 で残しておきたいデータをファイルに保存している場合は、AI エージェントに次のように依頼できます。

このアプリのデータが、Keelson で再起動・再デプロイした後も残るようにしてください。ファイル SQLite を使っている箇所を確認し、Managed SQLite への変更と、既存データの移し方を説明してください。

デプロイ時の検査でファイル SQLite の利用が検出されると、宣言がなければ拒否されます。再生成できるキャッシュ用途に限り、db.local_sqlite で一時ファイルの利用を宣言できます。この設定でもファイルは再起動後に残りません。詳しくはリファレンスを参照してください。

keelson.yaml に次の設定を書くと、アプリ専用のデータベースが用意されます。通常は AI エージェントが設定と接続コードを作成します。

db:
mode: libsql
  • DB サーバーの準備は不要です。 接続 URL と認証トークンは、Keelson がアプリの環境変数に設定します。
  • アプリごとにデータを分けます。 接続用の認証情報でアクセスできるのは、そのアプリの DB だけです。
  • 保存を確定したデータは再起動後も残ります。 SQL のコミットが完了した時点で保存されます。
  • アプリと同じリージョンに配置されます。 現在選べる配置先は日本のみです(region)。米国西海岸は準備中です。
  • バックアップから戻せます。 日次バックアップ、手動バックアップ、指定時刻への復元に対応しています。

設定ファイルの変更に加えて、アプリ側も libSQL クライアントで DB に接続する必要があります。SQL は SQLite と同じように書けます。Keelson 上では、ローカルのファイルパスではなく、Keelson が渡す接続 URL と認証トークンを使います。Python / Node.js では、環境変数がないときに file:local.db へフォールバックする形にしておくと、ローカル開発では同じコードがファイル SQLite として動きます(Go の libSQL クライアントは file: を開けないため、この形は使えません)。対応クライアントは Python・Node.js・Go で利用できます。

以下はテーブルを作り、データを 1 件保存する例です。Python は libsql、Node.js は @libsql/client を依存パッケージに追加します。

import os
import libsql
conn = libsql.connect(
database=os.environ.get("KEELSON_DB_URL", "file:local.db"), # file: = ローカル開発
auth_token=os.environ.get("KEELSON_DB_AUTH_TOKEN", ""),
)
conn.execute("""
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute("INSERT INTO items (name) VALUES (?)", ("サンプル",))
conn.commit()
import { createClient } from "@libsql/client";
const db = createClient({
url: process.env.KEELSON_DB_URL ?? "file:local.db", // file: = ローカル開発
authToken: process.env.KEELSON_DB_AUTH_TOKEN,
});
await db.execute(`
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
)
`);
await db.execute({ sql: "INSERT INTO items (name) VALUES (?)", args: ["サンプル"] });

テーブルや列を追加・変更する

Section titled “テーブルや列を追加・変更する”

テーブルや列の名前、データ型、制約など、DB の構造を定義したものを「スキーマ」と呼びます。テーブルがまだなければ、上の例のように CREATE TABLE IF NOT EXISTS で作成できます。

デプロイに合わせてテーブルや列を変更する場合は、db.migrate に変更用のコマンドを書きます。この処理を「マイグレーション」と呼びます。

db:
mode: libsql
migrate: "python migrate.py"

db.migrate は新しいコードに切り替える前に実行されます。失敗した場合は、それまでのコードでアプリの提供を続けます。ただし、DB に加えた変更は自動では戻りません。

同じコマンドを繰り返し実行しても、テーブルやデータの重複などが起きないようにします(冪等性)。変更後の DB は、それまでのコードでも読み書きできる状態にしてください。command のない静的サイトでは db.migrate は実行されません。詳しくは更新と復元を参照してください。

SQLite は、同時に書き込みを行う処理を 1 つずつ実行します。Web アプリと定期実行ジョブなどが同時に書き込むと、待ち時間が発生することがあります。書き込みが集中し、応答時間に影響する場合は、処理の見直しや外部の PostgreSQL などを検討してください。

すでに使っている PostgreSQL、MySQL、外部の libSQL などに接続する場合は、db.mode: none にします。接続情報は自分で用意し、シークレットとして設定してください。この設定で Keelson が外部 DB を作成したり、既存のデータを移したりすることはありません。

db:
mode: none
secrets:
items:
- name: DATABASE_URL
description: "PostgreSQL の接続文字列"
required:
- all_of: [DATABASE_URL]

Django は、この外部 DB 経路でのみデプロイできます(Managed SQLite には Django ORM 用のバックエンドがありません)。詳しくはフレームワーク別の注意点を参照してください。

外部 DB を使う主なケースです。

ケース選択肢
データ量や必要な処理速度が Managed SQLite では対応できない外部 PostgreSQL
同時書き込みが非常に多いPostgreSQL、MySQL
BI ツールや基幹システムから直接参照する共有の PostgreSQL、データウェアハウス
Keelson のプランより長い期間のバックアップが必要外部のマネージド DB

誤ってデータを削除・更新した場合に備えて、すべてのプランでバックアップと復元を利用できます。

方法用途
日次バックアップ毎日自動で保存されるバックアップから戻す
手動スナップショット大きな変更の前など、必要なタイミングでバックアップを作る(1 アプリ 1 日 5 回まで。日付は UTC 基準)
時刻復元(PITR)戻せる期間内の時刻を指定し、その時点のデータに戻す
ダウンロードバックアップや指定時刻のデータを手元に保存する

保存されるバックアップと戻せる期間

Section titled “保存されるバックアップと戻せる期間”
StarterPlusTeam
日次バックアップの保存数1 回分3 回分7 回分
時刻を指定して戻せる期間過去 24 時間過去 7 日過去 14 日

コンソールのアプリ画面で「復元ポイント」を開き、タイムライン上のバックアップや時刻を選ぶと、復元やダウンロードができます。

復元はアプリ単位で、他のアプリには影響しません。復元前の状態は 72 時間保持され、その間は復元を取り消せます。復元の実行には、ブラウザでの承認が必要です。

このバックアップで戻せるのは Managed SQLite のデータだけです。Files / Media SDK のファイル、外部 DB、アプリのコードや設定は対象外です。

たとえば、DB に保存した添付ファイルの ID を復元しても、Media から削除済みのファイル本体は戻りません。コードや設定を含めた変更の戻し方は更新と復元を参照してください。

  • Managed SQLite にはプランごとの固定上限はありません。ただし、基盤側の技術上限は適用されます。また、平常の業務利用を著しく超える負荷や、プラットフォームの安定運用・他のお客様の利用・当社の合理的な運用コストに影響を及ぼす利用を検知した場合、アプリの実行を制限または停止することがあります。緊急時は事前の通知なく制限することがあります
  • ワークスペースのストレージ(Files / Media SDK のファイル、スナップショット、デプロイ用ファイルなどの合計)はプランごとに上限があります。DB の容量はこれとは別枠です

プラン別の制限はプランと制限を参照してください。

ファイルとメディア

アプリの再起動・再デプロイ後も残したいファイルは、Files SDK または Media SDK で保存します。SDK の概要は Keelson SDK を参照してください。

アプリ内部で読み書きするファイルには Files、画面に表示したり利用者が開いたりするファイルには Media を使います。 Media に保存すると、ブラウザから直接開ける URL を使えるため、画像表示や添付ファイルの閲覧に適しています。

Files SDKMedia SDK
主な用途アプリ内部の処理に使うファイルを保存・更新する利用者に画像を見せる、PDF や添付ファイルを開いてもらう
例設定 JSON、処理済み URL の一覧、中間集計ファイル商品写真、申請書 PDF、利用者向けのレポート
読み取り方サーバー側のコードが SDK で読み取る。配信用 URL は付かないサーバー側のコードで読み取れるほか、ブラウザから URL で開ける
更新方法同じ名前(キー)で上書きできる。更新頻度の目安は 1 キーにつき 1 秒に 1 回程度上書きせず、新しいファイルとして保存するたびに ID が付く
1 ファイルの上限10 MiB50 MiB

ファイル形式だけで決める必要はありません。たとえば CSV でも、アプリ内部で集計に使うなら Files、利用者に渡すレポートなら Media が適しています。

Media の URL は誰でも開ける公開 URL ではありません。そのアプリを閲覧できるメンバーだけが、Keelson にログインして開けます。 Files も Media も、利用には keelson.yaml への追加設定は不要です。

Media の閲覧権限はアプリ単位です。「本人の添付ファイルだけを見せる」など、ファイルごとの権限判定は自動では行われません。そのような場合は Files に保存し、アプリ側で権限を確認してからファイルを返す方法を検討してください。

再起動後もファイルを残すには

Section titled “再起動後もファイルを残すには”

アプリの実行中にローカルファイルへ保存したデータは、再起動・再デプロイ後に引き継がれません。/tmp やアプリディレクトリに書くだけでは、後で使うファイルを残せません。

Files / Media SDK で保存が完了すると、ファイルはアプリの再起動後も残ります。後から同期処理を行う必要はありません。顧客情報や案件など、項目ごとに検索・更新するデータには Managed SQLite を使ってください。

Keelson SDK のインストール方法に従い、Files または Media をアプリの依存パッケージに追加します。以下のコード例はサーバー側で実行します。通常は AI エージェントに、保存したいファイルと用途を伝えて実装を依頼できます。

Files は、アプリが名前を付けて保存し、必要に応じて同じ名前で更新するファイルに向いています。Web アプリと定期実行ジョブの間で、保存したファイルを共有できます。

保存先の名前を「キー」と呼びます。settings.json や reports/2026-08.csv のように指定します。Node.js / Python の主な操作は、保存する write、読み取る read、削除する delete、キーの一覧を取得する list です。read はファイルがなければ、Node.js では null、Python では None を返します。

次は、設定を保存して読み取り、レポートの一覧取得と削除を行う例です。

import * as files from "@keelsonhq/files";
await files.write("settings.json", JSON.stringify({ theme: "dark" }));
const raw = await files.read("settings.json"); // Uint8Array | null
const settings = raw ? JSON.parse(new TextDecoder().decode(raw)) : {};
const keys = await files.list("reports/"); // ["reports/2026-08.csv", ...]
await files.delete("reports/2026-07.csv");
import json
from keelson import files
files.write("settings.json", json.dumps({"theme": "dark"}))
settings = json.loads(files.read("settings.json") or "{}") # read() -> bytes | None
keys = files.list("reports/")
files.delete("reports/2026-07.csv")

ローカルファイルから移す場合は、書き込みと読み取りの両方を SDK に変更します。Files には配信用 URL がないため、保存したファイルを利用者に渡したい場合は、アプリ側にダウンロード処理を実装するか、Media を使ってください。

複数の処理が同じキーを読み取って更新すると、一方の変更をもう一方が上書きすることがあります。同時更新が必要な処理状況やカウンターなどは、DB のトランザクションで管理してください。

Media は、アプリの画面で見せる画像や、利用者が開く PDF・添付ファイルに向いています。保存したファイルを Keelson が URL で配信するため、アプリ側にファイル配信用の処理を書く必要はありません。

  1. サーバー側のコードで put を呼び、ファイルを保存します。
  2. 返されたファイル ID を、案件や商品などのデータと一緒に DB に記録します。
  3. url(id) で URL を取得し、画面の画像やリンクに設定します。ブラウザがその URL にアクセスすると、Keelson が認証を確認してファイルを返します。

画像を保存するサーバー側の関数の例です。引数には、アップロードで受け取った画像データを渡します。

import * as media from "@keelsonhq/media";
async function savePhoto(imageBytes) {
const id = await media.put(imageBytes, {
contentType: "image/png",
filename: "photo.png",
});
// id を DB に記録し、url を画面に返します。
return { id, url: media.url(id) };
}
from keelson import media
def save_photo(image_bytes):
file_id = media.put(
image_bytes,
content_type="image/png",
filename="photo.png",
)
# file_id を DB に記録し、url を画面に返します。
return {"id": file_id, "url": media.url(file_id)}

Keelson 上での URL は /__keelson/media/<ファイルID> という形式です。たとえば、画像の URL を <img> の src に、PDF の URL をリンクの href に設定します。

<!-- 保存したファイルの URL を設定する例 -->
<img src="/__keelson/media/画像のファイルID" alt="商品の写真" />
<a href="/__keelson/media/PDFのファイルID">申請書を開く</a>

ファイルの読み取りは Keelson が処理します。URL を知っているだけでは開けず、そのアプリの閲覧権限が必要です。

保存済みのファイルは上書きできません。差し替える場合は新しいファイルを保存し、DB に記録した ID を更新します。不要になった古いファイルは別途削除してください。

  • サーバー側で中身を読み取るには get(id)、種類(Content-Type)とサイズを調べるには stat(id) を使います。
  • 存在確認は exists(id)、削除は Node.js では del(id)、Python では delete(id) です。
  • Content-Type を省略すると、ファイル名の拡張子から推測します。推測できない場合は application/octet-stream になります。

手元で開発するときは、Keelson の接続設定がなければ、どちらの SDK もローカルのフォルダにファイルを保存します。手元のファイルが、デプロイ時に Keelson の Files / Media に自動で移されるわけではありません。

  • Files: ./.keelson/files/ 配下に実ファイルとして書きます。.keelson/ を .gitignore に入れてください
  • Media: ./media/ 配下に書きます(MEDIA_DIR で変更可)

Keelson 上では、必要な環境変数を Keelson が設定します。

Media のローカルモードでは、URL の既定値は /media/<ファイルID> です。ブラウザで表示を確認するには、開発用サーバーにファイルを返す処理を用意してください。media.url() は URL を返す関数で、ローカルの HTTP 配信を開始するものではありません。

Files SDK と Media SDK のローカルモードは、Node.js / Python / Go のいずれも Linux、macOS、Windows で利用できます。

Files SDK はキーをそのままローカルのファイルパスに対応させます。大文字小文字を区別しない設定の Windows / macOS では、Report と report のように大文字小文字だけが異なるキーを同時には保存できません。パスベースで処理する環境には、チェック後に別のプロセスが symlink や junction を差し替える競合余地があります。信頼できる単一ユーザーのローカル開発用として利用してください。本番の GCS 保存経路にはこの制限はありません。

  • コンソールや CLI には、アプリのファイルを一覧表示・ダウンロード・差し替える機能はありません。利用者向けのファイル一覧はアプリに用意します。Media は保存した ID から閲覧用リンクを作り、Files は必要に応じてダウンロード処理を実装してください。
  • 上限を超える大容量ファイルは、S3 互換などの外部オブジェクトストレージにアプリから直接保存してください(SMTP ポートを除き、外部通信に制限はありません)

Files / Media のファイルはワークスペースのストレージ容量(プランと制限)に含まれます。

Managed SQLite のバックアップには、Files / Media のファイル本体は含まれません。DB を復元しても、削除したファイルは戻りません。

定期実行ジョブ

毎朝のレポート作成や、1 時間ごとのデータ同期など、決まった時刻・間隔で処理を動かせます。この処理を「定期実行ジョブ」と呼びます。Web アプリへのアクセスがなくても実行されます。

通常は AI エージェントに、実行したい処理とスケジュールを伝えると、keelson.yaml の crons に設定します。

ワークスペースのタイムゾーンで毎朝 9 時に、前日分の売上を集計する定期実行ジョブを追加してください。実行結果をログで確認できるようにしてください。

Web アプリに、毎日 9 時に report.py を実行するジョブを追加した例です。

slug: my-app
runtime: python-slim
command: "python app.py"
db:
mode: libsql
crons:
- name: daily-report
description: "前日の売上レポートをメールで送る"
schedule: "0 9 * * *" # 毎日 9:00
command: "python report.py"
timeout: 120 # 1 回の実行を最大 120 秒にする
項目意味
nameジョブの名前。小文字英数字とハイフンで指定します
schedule実行する時刻・間隔を表す cron 式
command1 回分の処理を実行し、終了するコマンド
timeout1 回の実行時間の上限(秒)。上限に達すると停止します
enabledfalse にすると定期実行を止めます。省略時は true です
description任意。ジョブが何をするかの 1 行説明。コンソールの定期実行タブの「説明」列に表示されます。前後の空白は除かれ、空なら未指定と同じです。最大 100 文字。表示専用で、実行内容には影響しません

name、schedule、command は必須です。timeout は 1〜600 秒の範囲で、プランの上限以下を指定してください。省略時は 300 秒ですが、プランの上限がそれより短ければ、その上限が適用されます。上限を超える値を明示すると、デプロイは拒否されます。

Web アプリなしで、定期実行だけのアプリも作れます。その場合はトップレベルの command を省略します。定期実行だけのアプリは「同時に使えるアプリ数」の枠を消費しません。

schedule には、時刻や間隔を 5 つの項目で表す「cron 式」を書きます。

やりたいことcron 式
毎日 9:000 9 * * *
平日 9:000 9 * * 1-5
毎時 0 分0 * * * *
15 分ごと(Plus / Team)*/15 * * * *
5 分ごと(Team)*/5 * * * *
毎月 1 日 0:000 0 1 * *

スケジュールはワークスペースのタイムゾーンで評価されます。タイムゾーンはワークスペース作成時にブラウザから設定され、後から変更できません。実行開始は予定時刻より遅れることがあります。

最小間隔は Starter 60 分 / Plus 15 分 / Team 5 分です。それより短いスケジュールはデプロイ時に拒否されます。毎分の実行には対応していません。

cron 式の各項目
┌───────────── 分(0–59)
│ ┌─────────── 時(0–23)
│ │ ┌───────── 日(1–31)
│ │ │ ┌─────── 月(1–12)
│ │ │ │ ┌───── 曜日(0–6、0=日曜)
* * * * *

* はすべての値を表します。たとえば 0 9 * * * は「毎月・毎日・すべての曜日の 9 時 0 分」です。

コンソールのアプリ画面で、ジョブごとの実行履歴、成功・失敗、ログを確認できます。「今すぐ実行」で、次の予定時刻を待たずに動作を確認することもできます。

失敗した場合はログを確認し、必要なシークレットや設定値がそろっているか、timeout 内に処理が終わるかを調べます。ログを AI エージェントに渡して、原因の調査と修正を依頼できます。

標準出力と標準エラーのログは、1 回の実行につきそれぞれ 256 KiB まで記録されます。超えた分は省略されます。

実際に開始した実行を数えます。成功・失敗・「今すぐ実行」は含み、スキップされた回は含みません。合計実行時間は、外部 API の待ち時間などを含む経過時間で数えます。platform 起因で実行時間を確定できなかった場合、合計実行時間には数えませんが、実行回数は 1 回消費します。どちらかの月間上限に達すると当月の残りの実行はスキップされます。すでに開始した実行は完了まで継続し、上限は翌月にリセットされます。

最大本数を最短間隔で動かすと、月間上限には収まりません。本数・最短間隔・1 回の実行時間・月間実行回数・合計実行時間は、それぞれ独立した上限です。

プランごとの上限はプランと制限を参照してください。

定期実行ジョブは、1 回分の処理を行って終了します。アプリを動かし続けるための機能ではありません。

項目動作
Web アプリとの関係別の実行環境で動きます。メモリやローカルファイルは共有しません
データの保存実行環境内のファイルは次回に引き継がれません。残したいデータは Managed SQLite や Files / Media SDK に保存します
前回の実行が終わっていない場合同じジョブの次の実行をスキップします。後で実行するための予約は残りません
実行に失敗した場合自動ですぐに再試行はしません。次の予定時刻には通常どおり実行します
実行時間の上限に達した場合処理の途中でも停止します

アプリ内のタイマーやスケジューラは、アプリが停止している間には動作しません。決まった時刻の処理には crons を使ってください。レスポンスを返した後のバックグラウンド処理も、完了が保証されません。リクエスト内で完了させるか、レスポンスの後に続ける処理はバックグラウンドタスクに投入します。

詳しくはアプリが動くタイミングを参照してください。

再実行してもデータが重複しないようにする

Section titled “再実行してもデータが重複しないようにする”

途中で停止した処理を次回やり直したり、「今すぐ実行」で同じ処理を繰り返したりしても、登録や送信が重複しないようにします。この性質を「冪等性」と呼びます。

たとえば、reports.date に主キーまたは UNIQUE 制約があるテーブルなら、同じ日付のデータがあれば更新できます。

conn.execute("""
INSERT INTO reports (date, total) VALUES (?, ?)
ON CONFLICT(date) DO UPDATE SET total = excluded.total
""", (today, total))
conn.commit()

タスクを使わずに受け付けた処理を後で実行する

Section titled “タスクを使わずに受け付けた処理を後で実行する”

受け付けた処理をリクエストの後すぐに実行するなら、まずバックグラウンドタスクを使ってください。タスクを使わずに組む場合や、次の実行までの待ち時間を許せる場合は、次の方法を使います。

Web アプリで受け付けた処理を後でまとめて実行する場合は、未処理の内容を Managed SQLite に保存します。定期実行ジョブが未処理のデータを読み取り、処理してから完了を記録します。

# Team プランで 5 分ごとに実行する例
crons:
- name: process-pending
schedule: "*/5 * * * *"
command: "python process_pending.py"
timeout: 120
  • 開始までの待ち時間を考慮します。 次のジョブ実行まで待つため、すぐに開始する必要がある用途には適していません。利用者にも待ち時間を案内してください。
  • 同じ処理が繰り返される場合に備えます。 メール送信などが成功しても、完了を DB に記録する前にジョブが止まることがあります。
  • 失敗の回数と理由を記録します。 再試行の上限を設け、繰り返し失敗するデータをどう扱うか決めてください。Keelson が個々の未処理データを自動で再試行・隔離するわけではありません。

リクエスト内で完了できる短い処理は、レスポンスを返す前に済ませてください。

大量のデータを処理するときは、1 回の件数に上限を設け、残りは次回に処理します。実行時間の上限を超える処理は、複数回に分けてください。

外部 API を呼ぶ場合は、相手のサービスが定める呼び出し回数の制限にも合わせます。間隔を広げるか、1 回あたりの呼び出し回数を減らしてください。

sales と reports テーブルが作成済みで、reports.date に主キーまたは UNIQUE 制約がある例です。依存パッケージに libsql を追加します。

この例では、日本時間で前日分を集計します。sales.date には同じタイムゾーンで求めた日付を YYYY-MM-DD 形式で保存している前提です。Asia/Tokyo は集計に使うタイムゾーンに合わせて変更してください。ワークスペースのスケジュール設定が、コードで日付を求める際のタイムゾーンを自動で変更するわけではありません。

report.py
import os
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import libsql
conn = libsql.connect(
database=os.environ["KEELSON_DB_URL"],
auth_token=os.environ["KEELSON_DB_AUTH_TOKEN"],
)
report_date = (datetime.now(ZoneInfo("Asia/Tokyo")).date() - timedelta(days=1)).isoformat()
row = conn.execute(
"SELECT COUNT(*), COALESCE(SUM(amount), 0) FROM sales WHERE date = ?", (report_date,)
).fetchone()
conn.execute(
"INSERT INTO reports (date, count, total) VALUES (?, ?, ?) "
"ON CONFLICT(date) DO UPDATE SET count = excluded.count, total = excluded.total",
(report_date, row[0], row[1]),
)
conn.commit()
print(f"集計完了: {report_date} {row[0]} 件 / {row[1]}")

依存パッケージに @libsql/client を追加し、SYNC_API_URL を環境変数に設定します。synced_items テーブルが作成済みで、id に主キーまたは UNIQUE 制約があり、同期先の API が id を持つオブジェクトの配列を返す例です。

sync.js
import { createClient } from "@libsql/client";
const db = createClient({
url: process.env.KEELSON_DB_URL,
authToken: process.env.KEELSON_DB_AUTH_TOKEN,
});
const res = await fetch(process.env.SYNC_API_URL);
if (!res.ok) throw new Error(`同期先 API のエラー: ${res.status}`);
const items = await res.json();
for (const item of items) {
await db.execute({
sql: `INSERT INTO synced_items (id, data) VALUES (?, ?)
ON CONFLICT(id) DO UPDATE SET data = excluded.data`,
args: [item.id, JSON.stringify(item)],
});
}
console.log(`同期完了: ${items.length} 件`);

通知処理を notify.py に実装した場合の設定例です。Webhook URL はシークレットで渡します。

slug: daily-notifier
runtime: python-slim
db:
mode: none
secrets:
items:
- name: SLACK_WEBHOOK_URL
description: "通知先の Slack Incoming Webhook"
required:
- all_of: [SLACK_WEBHOOK_URL]
crons:
- name: daily-notify
schedule: "0 9 * * 1-5"
command: "python notify.py"
timeout: 30
状況対処
timeout に 600 秒を超える値、またはプラン上限を超える値を指定した上限以下にし、必要なら処理を分割する
スケジュールがプランの最小間隔より短い間隔を広げる
ジョブの本数がプラン上限を超えている処理をまとめるか、不要なジョブを削除する
description が 100 文字を超えている(cron_description_too_long)100 文字以内の 1 行に縮める。自動では切り詰めない
type: web と crons を併用している定期実行だけなら type を外す。静的サイトに処理を追加する場合は、サーバーを持つ構成への変更も確認する
廃止された workers: が残っている削除し、処理に応じて crons かリクエスト処理に変更する
  • イベント発生後、すぐに開始する必要がある処理。定期実行では次の予定時刻まで待ちます。バックグラウンドタスクを使ってください。
  • 分割できず、1 回の実行時間の上限を超える処理。
  • ジョブの完了を待って別のジョブを開始するなどの、ワークフロー制御。

バックグラウンドタスク

PDF の生成や外部 API の重い呼び出しなど、リクエストを受けた後に時間のかかる処理を、Web アプリとは別のインスタンスで実行できます。この処理を「バックグラウンドタスク」(以下「タスク」)と呼びます。

keelson.yaml の tasks にコマンドを宣言し、アプリのコードから Keelson SDK の enqueue で投入すると、Keelson がそのコマンドを 1 回起動します。失敗した場合は自動で再試行します。

通常は AI エージェントに、リクエストの後に続けたい処理を伝えると、tasks の宣言と投入のコードを書きます。

注文を受け付けたら、領収書の PDF を作ってメールで送る処理をバックグラウンドタスクにしてください。同じタスクが 2 回走っても、PDF とメールが重複しないようにしてください。

処理使うもの
フォームの保存など、リクエスト中に終わる短い処理リクエスト処理の中で完了させる
毎朝のレポートなど、時刻で動かす処理定期実行ジョブ(crons)
リクエストを受けて、後で走らせる処理バックグラウンドタスク(tasks)

タスクが向いているのは、次のような処理です。

  • HTTP リクエストの上限(120 秒)に収まらない処理
  • 外部 API の重い呼び出し(AI の生成・大量のデータ送信など)
  • PDF・画像の生成、メールの一斉送信

レスポンスを返した後にアプリの中で続ける処理(FastAPI の BackgroundTasks など)は、最後まで実行されることが保証されません。こうした処理はタスクに移します。

keelson.yaml の tasks に、タスクの名前と実行するコマンドを書きます。

slug: my-app
runtime: python-slim
command: "python app.py"
db:
mode: libsql
tasks:
- name: generate-pdf
description: "注文の請求書 PDF を作る"
command: "python generate_pdf.py"
timeout: 180 # 1 回の試行を最大 180 秒にする(全プランで有効)
max_attempts: 3 # 初回を含めて最大 3 回まで試行する
項目意味
nameタスクの名前。小文字英数字とハイフンで指定します。投入時にこの名前を指定します
command1 回分の処理を実行し、終了するコマンド
timeout1 回の試行の制限時間(秒)。省略時は 300 秒(プランの上限がそれより短ければ上限)。上限を超える値を書くとデプロイが拒否されます
max_attempts初回を含めた試行の最大回数。省略時は 3 回
description任意。タスクが何をするかの 1 行説明。コンソールのバックグラウンドタスクタブに表示されます。前後の空白は除かれ、空なら未指定と同じです。100 文字を超えるとデプロイが拒否されます(task_description_too_long。切り詰めはしません)。表示専用で、実行内容には影響しません

name と command は必須です。項目の範囲や検証のルールは keelson.yaml リファレンスを参照してください。

アプリのコードから、宣言した名前と入力データを指定して投入します。投入はすぐに返り、タスクの ID(task_id)が返ります。SDK の導入方法は Keelson SDK を参照してください。

from keelson import tasks
task_id = tasks.enqueue("generate-pdf", {"order_id": 1},
idempotency_key="order-1-pdf")
status = tasks.get(task_id)
print(status.status, status.claimed_attempts, status.last_failure_code)
try:
tasks.enqueue("generate-pdf", {"order_id": 2})
except tasks.TasksError as e:
if e.code == "TASK_MONTHLY_QUOTA_EXCEEDED":
... # 今月の枠を使い切った

idempotency_key(冪等キー)を渡すと、同じタスク名・同じキーで投入し直しても新しいタスクは作られず、最初のタスクの ID が返ります。キーは 1〜128 文字の英数字・記号です。

get が返す状態(status)は次の 5 つです。

状態意味
queued未処理。開始待ち・再試行待ちを含みます
running実行中
succeeded成功した(終了コード 0)
failed失敗した。理由は last_failure_code で分かります(失敗したとき)
cancelled開始前に取り消された

claimed_attempts は開始した試行の数、last_failure_code は最後に失敗した試行の理由です。

完了を画面に出したいときは、タスクのコマンドが結果をデータベースに書き、画面はデータベースの状態列を見る作りにします。get は確認の補助として使います。

タスクのコマンドは、普通のプログラムです。Keelson はコマンドを起動すると、標準入力に次の JSON を 1 行書いてから閉じます。

{"attempt_no":1,"payload":{"order_id":1},"schema_version":1,"task_id":"…","task_name":"generate-pdf"}
キー内容
attempt_no何回目の試行か(1 から)
payload投入時に渡した入力データ(省略時は null)
schema_versionこの入力の形式の版。現在は 1
task_idタスクの ID。再試行しても変わりません
task_nameタスクの名前

コマンドが終了コード 0 で終われば成功、それ以外は失敗です。

import json
import sys
doc = json.loads(sys.stdin.readline())
order_id = doc["payload"]["order_id"]
# ... PDF を作って保存する ...
# 例外で終わると終了コードが 0 以外になり、失敗として扱われる

タスクは Web アプリとはローカルディスクを共有しません。結果はデータベースか Files / Media SDK に保存してください。コマンドが読める環境変数は定期実行ジョブと同じです。

2 回走っても壊れないように書く

Section titled “2 回走っても壊れないように書く”

タスクは少なくとも 1 回実行されます。失敗した試行は自動で再試行され、成功した後でも終了の連絡が Keelson に届かなければ、もう一度走ることがあります。同じタスクのコマンドが 2 回以上走っても結果が壊れないように(冪等に)書くのは、アプリ側の責任です。

次の 3 つを守ります。

  1. 状態を比較して書き換える:「未処理なら処理中にする」を、1 文の UPDATE で行います。更新した行が 0 件なら、別の試行がすでに処理しているので終了します。

    UPDATE orders SET pdf_status = 'processing'
    WHERE id = ? AND pdf_status = 'pending';
  2. 外部 API には冪等キーを渡す:決済やメール送信など、同じ依頼を 2 回受けると困る外部 API には、冪等キーを渡します。再試行しても変わらない task_id を使えます。

  3. 出力のファイル名や行のキーを固定する:receipts/order-1.pdf のように、入力から決まる名前で保存します。日時や乱数を含む名前にすると、2 回走ったときに出力が 2 つできます。

注文を保存したら必ず処理する

Section titled “注文を保存したら必ず処理する”

「注文を保存したら、必ず領収書のタスクも投入する」ようにしたい場合、注文の保存とタスクの投入を 1 つのトランザクションにはできません。注文はアプリのデータベースに、タスクは Keelson 側に記録されるためです。保存の直後にアプリが止まると、注文だけが残ってタスクが投入されません。

これを防ぐには、注文と同じトランザクションで「まだ投入していないタスク」の行(送信待ち行)をアプリのデータベースに書いておき、commit の後に投入します。一般に outbox パターンと呼ばれる方法です。

  1. 1 つのトランザクションで、注文の行と送信待ち行(ID・タスク名・入力・投入済みの印)を書く
  2. commit の後、送信待ち行の ID を idempotency_key にして enqueue する
  3. 投入に成功したら、送信待ち行に投入済みの印を付ける
  4. 投入に失敗した行や、印を付ける前にアプリが止まった行は、次のリクエストか定期実行ジョブで同じキーのまま送り直す。キーが同じなので、二重に投入されることはありません
Web(リクエスト処理) アプリのデータベース Keelson(タスク)
│ ① 注文の行 + 送信待ち行を 1 つのトランザクションで書く
│ ─────────────────────────────▶ │
│ ② enqueue(名前, 入力, idempotency_key = 送信待ち行の ID)
│ ────────────────────────────────────────────────────────────▶ │
│ ◀──────────────────────────────────────────────── task_id ──── │
│ ③ 送信待ち行に投入済みの印を付ける
│ ─────────────────────────────▶ │
④ ②③の前に失敗・停止した行は、次のリクエストか cron が同じキーで送り直す(重複しない)
import json
import uuid
from keelson import tasks
def place_order(conn, order_id, items):
outbox_id = str(uuid.uuid4())
conn.execute("INSERT INTO orders (id, items) VALUES (?, ?)", (order_id, items))
conn.execute(
"INSERT INTO task_outbox (id, task_name, payload, enqueued) VALUES (?, ?, ?, 0)",
(outbox_id, "send-receipt", json.dumps({"order_id": order_id})),
)
conn.commit()
flush_outbox(conn)
def flush_outbox(conn):
# 次のリクエストや定期実行ジョブからも呼ぶ
rows = conn.execute(
"SELECT id, task_name, payload FROM task_outbox WHERE enqueued = 0"
).fetchall()
for outbox_id, name, payload in rows:
tasks.enqueue(name, json.loads(payload), idempotency_key=outbox_id)
conn.execute("UPDATE task_outbox SET enqueued = 1 WHERE id = ?", (outbox_id,))
conn.commit()

自動の再試行:コマンドが走って失敗した場合(exit_nonzero・timed_out・no_completion)、試行回数が max_attempts に達していなければ、10 秒・20 秒・40 秒・80 秒の間隔を空けて再試行します。間隔は指定できません。

コマンドが走る前に終わった場合(delivery_failed・manifest_unresolvable・quota_exhausted)は、再試行せずにそのまま failed になります。

last_failure_code意味
exit_nonzeroコマンドが 0 以外の終了コードで終わった
timed_out制限時間(timeout)を超えて打ち切られた
no_completion終了の連絡が無いまま期限を過ぎた
delivery_failed実行の準備ができてから 1 時間以内に試行を開始できなかった
manifest_unresolvable待っている間にデプロイが 2 回以上重なり、試行に使う宣言を解決できなかった
quota_exhausted再試行の分、または月をまたいで開始する分の月間枠を確保できなかった

原因を調べる:コンソールのアプリ画面の「バックグラウンドタスク」タブで、タスクの状態と、最後の試行の標準エラー(256 KiB まで。シークレットの値は伏せて表示)を確認できます。標準エラーは SDK の get では返りません。

やり直す:手動の再実行はありません。やり直すには、アプリから投入し直します。冪等キーを付ける場合は、元のタスクとは別のキーにします(同じキーでは元のタスクが返ります)。

取り消されるとき:次の操作をすると、対象の queued のタスクは cancelled になります。実行中の試行は最後まで走りますが、失敗しても再試行しません。

  • アプリの手動のサスペンド
  • 不正利用によるアプリの隔離
  • アプリの削除(タスクの記録も消えます)
  • tasks からその名前を外すデプロイ(外した名前のタスクだけ)

このほか、コンソールの「バックグラウンドタスク」タブから、指定した queued のタスクを 1 件ずつ取り消せます。アクセスが無いことによるアプリのスリープでは、タスクは取り消されません。

項目上限
宣言数アプリあたり 10
未処理(queued)のタスクアプリあたり 1,000 件。超えると投入が TASK_BACKLOG_LIMIT_EXCEEDED で拒否されます
1 回の試行の実行時間既定 300 秒・最大 600 秒。プランごとの上限は Starter 180 秒 / Plus 300 秒 / Team 以上 600 秒
試行回数既定 3 回・最大 5 回
入力の大きさ投入要求全体(payload と idempotency_key を JSON にしたもの)で 65,536 bytes。超えると TASK_PAYLOAD_TOO_LARGE
入力の保存期間タスクが終わってから 7 日で削除
月間枠定期実行ジョブと合わせた「バックグラウンド実行」の回数と合計実行時間

timeout を省略したときの既定(300 秒)がプランの上限を超える場合は、上限に切り下げます。上限を超える値を明示すると、デプロイは拒否されます。

月間枠では、開始した試行 1 回を 1 回と数え、再試行も 1 回ずつ数えます。上限に達すると、新規の投入は TASK_MONTHLY_QUOTA_EXCEEDED で拒否されます。同じ冪等キーでの投入し直しは、上限に達していても最初のタスクを返します。

プランにより同時に走るタスクの数に上限があり、超えた分は順番を待ちます。月間枠の数値はプランと制限を参照してください。

タスクは Web アプリのリクエスト処理だけでなく、定期実行ジョブやタスクのコマンドからも投入できます。たとえば、毎晩の定期実行ジョブで対象を探し、1 件ずつタスクに投入できます。

認証は Keelson が自動で行います。コードにトークンを書く必要はありません。

Keelson の外(KEELSON_MODE=local を指定したとき、または Keelson の環境変数が無いとき)では、SDK の enqueue が CLI の keelson dev task run を子プロセスとして起動し、コマンドが終わるまで待ってから返ります(同期実行)。

本番とは次の点が違います。

  • enqueue が、処理が終わるまで返りません
  • 別のインスタンスではなく、同じ開発機の子プロセスで走ります。本番では見えないローカルのファイルが見えてしまいます
  • 再試行しません。コマンドが失敗しても enqueue はエラーにならず、get が failed を返します
  • 同時実行の制限・未処理件数の上限・月間枠がありません
  • 実行時間はプランで切り下げず、宣言した timeout をそのまま使います
  • 冪等キーで重複を防げるのは、同じプロセスの中で順に投入した場合だけです。同時の投入・別のプロセス・再起動の後は防ぎません
  • get で引けるのは、同じプロセスの中で投入したタスクだけです

手元で通っても、本番で同じ結果になるとは限りません。本番では再試行やプランの上限がかかり、試行が 2 回以上走ることがあります。

手元で動かすには、keelson dev task run を含む版の CLI が必要です。古い CLI では enqueue が TASKS_LOCAL_CLI_FAILED になるので、keelson upgrade で更新してください。

アプリを通さずにコマンドだけを試すこともできます。

Terminal window
keelson dev task run generate-pdf --payload '{"order_id": 1}'

ログイン中のユーザー情報を使う

Keelson がログインと、アプリを開ける人の確認を行います。アプリでは、そのユーザー情報を使って、作成者を記録したり、本人のデータだけを表示したりできます。

「誰がどの申請を承認できるか」など、アプリ内の業務ルールはアプリ側で実装します。用途に応じて、次の 3 つの方法を使い分けます。

やりたいこと使うもの追加設定
誰がアクセスしているか知りたい(作成者の記録、自分のデータだけ表示)X-Keelson-User-Id などのユーザー情報ヘッダー不要
アプリの運用者(デプロイ・設定変更ができる人)だけに見せる画面を作りたいヘッダー X-Keelson-User-App-Perms不要(権限はコンソールで設定)
部署ごと・役職ごとの分岐、承認者の判定Identity SDK の attributes.groupskeelson apps directory enable

「上司だけが承認できる」のような業務上の権限は、アプリ管理用の manage ではなく、所属グループ、またはアプリ自身のデータ(担当者テーブルなど)で判断します。manage を持つのはアプリをデプロイ・設定できる人であり、業務上の承認者と一致するとは限りません。

Keelson は、ログイン済みユーザーからのリクエストに、ユーザー情報をヘッダー(リクエストに付く付加情報)として渡します。アプリのサーバー側で読み取れます。

ヘッダー内容
X-Keelson-User-Idユーザー ID(安定した識別子。DB に保存するならこれ)
X-Keelson-User-Emailメールアドレス(Keelson に値がなければ空文字)
X-Keelson-User-Name表示名(Keelson に値がなければ空文字)

作成者を記録するなら、データの保存時にユーザー ID も記録します。本人のデータだけを表示するなら、DB の検索条件にその ID を使います。ヘッダーを読み取るだけで、データの絞り込みが自動で行われるわけではありません。

# FastAPI / Flask
user_id = request.headers.get("X-Keelson-User-Id")
email = request.headers.get("X-Keelson-User-Email")
// Express
const userId = req.headers["x-keelson-user-id"];
const email = req.headers["x-keelson-user-email"];

これらのユーザー情報は、Keelson がログインを確認したうえで付けます。呼び出し元が同名のヘッダーを送っても、Keelson が確認したユーザー情報に置き換えられます。アプリ側で Keelson のログインセッションを管理する必要はありません。

Identity SDK を使うと、ユーザー情報を id、email、name としてまとめて取得できます。導入方法は Keelson SDK のインストールを参照してください。以下の例はサーバー側で実行します。

getCurrentUser はヘッダーを読むだけで、外部への通信や Directory API の有効化は不要です。JavaScript では await を付けて呼びます。ユーザー ID のヘッダーがない場合や空の場合は例外(IdentityError)になります。

import { getCurrentUser } from "@keelsonhq/identity";
app.get("/", async (req, res) => {
const user = await getCurrentUser({ headers: req.headers }); // { id, email, name }
res.type("text").send(`こんにちは、${user.name ?? "ゲスト"} さん`);
});

このアプリでの権限(X-Keelson-User-App-Perms)もまとめて取得するなら getRequestUser(Python は get_request_user、Go は GetRequestUser)を使います。戻り値は getCurrentUser の値に perms を足したものです(使い方)。

アプリの権限は「閲覧(view)」と「管理(manage)」の 2 種類で、コンソールまたは CLI でグループに割り当てます(グループとアプリ権限)。manage はデプロイ・設定変更・シークレット編集ができる運用者の権限で、既定では Owner / Admin / Developer が持ちます。Keelson はその結果をヘッダーで渡します。

X-Keelson-User-App-Perms意味
view閲覧できる
view,manage閲覧と管理ができる

アプリは manage が含まれるかだけを見ます。グループ名はコードに書きません。

// Express — ヘッダーはリクエストごとに違うので、ハンドラの中で読む
function canManage(req) {
const perms = (req.headers["x-keelson-user-app-perms"] ?? "").split(",");
return perms.includes("manage");
}
app.get("/admin", (req, res) => {
if (!canManage(req)) return res.status(403).end();
res.send("運用者向けの画面");
});
# FastAPI / Flask — 同じくリクエストごとに読む
def can_manage(request) -> bool:
perms = request.headers.get("X-Keelson-User-App-Perms", "").split(",")
return "manage" in perms

「経理チームだけが使えるアプリ」は権限の割り当てだけで実現でき、アプリ側のコードは要りません(チームにアプリを共有する)。「運用者だけが設定画面を開ける」は manage の確認で実現できます。誰が manage を持つかは、コードを変えずにコンソールで変えられます。

部署ごと・役職ごとの分岐や、「承認者だけが承認できる」のような業務上の権限が必要なら、Identity SDK でユーザーの所属グループを取得します。

画面の出し分けだけでは不十分です。「本人の申請だけ見える」を実装するときは、一覧の絞り込みに加えて、API で他人の申請 ID を指定しても取得・更新できないことをサーバー側で確認してください。判定に使うのは、ブラウザから送られた値ではなく、Keelson が付けたヘッダーや SDK の戻り値です。

グループやメンバーの情報を取得するには、Directory API の読み取りを許可し、再デプロイします。有効にしただけでは、実行中のアプリはまだ利用できません。

Terminal window
keelson apps directory enable
keelson deploy

再デプロイすると、環境変数 KEELSON_DIRECTORY_TOKEN がアプリに渡され、SDK が自動で使います。トークンはサーバー側だけで使い、ブラウザに渡さないでください。

import { getCurrentIdentity } from "@keelsonhq/identity";
// リクエストを処理する関数の中で取得します。
const me = await getCurrentIdentity({ headers: req.headers });
const groups = me.attributes?.groups ?? []; // 例: ["everyone", "developers", "経理"]
const isAccountingMember = groups.includes("経理");
// この結果を使って、経理向けの操作を許可するか判断します。
from keelson import identity
me = identity.get_current_identity(headers=request.headers)
groups = (me.attributes.groups or []) if me.attributes else []
is_accounting_member = "経理" in groups
# この結果を使って、経理向けの操作を許可するか判断します。

attributes.groups に入るのはグループのキーです。

  • ロールに応じたシステムグループ: Owner は owners developers everyone、Admin は admins developers everyone、Developer は developers everyone、App User は everyone
  • そのアプリに権限が割り当てられているカスタムグループのうち、ユーザーが属するもの。アプリに割り当てていないグループは出ません
  • キーは変わらないので、コードに書いて判定に使えます。日本語のキー(経理)も使えます

getCurrentIdentity は Directory API に問い合わせます。戻り値には、ユーザー(user.id、user.email、user.name)、ワークスペースでのロール(workspace.role)、このアプリでの権限(app.permissions)も含まれます。getCurrentUser とはユーザー情報の参照方法が異なります。

担当者の選択肢にメンバー一覧を使う

Section titled “担当者の選択肢にメンバー一覧を使う”

担当者の選択肢を出すなど、他のメンバーの情報が必要なときは listMembers / list_members(検索、ロール・グループで絞り込み)、getUser、listGroups が使えます。

メンバーごとに id、email、name、role、image_url が返ります。image_url はプロフィール画像の URL(img.clerk.com から配信)で、画像が無いメンバーは null です。URL に width / height のクエリを付けると、縮小した画像を取得できます。

サーバー側では、選択肢に必要な項目だけを返します。

import { listMembers } from "@keelsonhq/identity";
// リクエストを処理する関数の中で取得します。1 回で最大 100 人です。
const page = await listMembers({ group_key: "developers", limit: 100 });
const options = page.items.map((m) => ({
id: m.id,
label: m.name || m.email,
image_url: m.image_url, // 文字列または null
}));
from keelson import identity
page = identity.list_members(group_key="developers", limit=100)
options = [
{"id": m.id, "label": m.name or m.email, "image_url": m.image_url}
for m in page.items
]

ブラウザ側では、画像が無いときや読み込みに失敗したときに頭文字を表示します。

function avatar(option) {
const initials = document.createElement("span");
initials.className = "avatar-initials";
initials.textContent = option.label.slice(0, 1).toUpperCase();
if (!option.image_url) return initials;
const img = document.createElement("img");
img.className = "avatar";
img.alt = "";
const src = new URL(option.image_url);
src.searchParams.set("width", "64");
src.searchParams.set("height", "64");
img.src = src.toString();
img.addEventListener("error", () => img.replaceWith(initials), { once: true });
return img;
}

アプリの DB には、メンバーの id だけを保存してください(タスクの担当者の列など)。image_url・name・email は保存しません。表示するたびに listMembers や getUser で最新の値を取得します。メンバーが画像を差し替えると、保存した値は古くなります。

手元で動かすときは Keelson のログインがないため、ユーザー情報のヘッダーが付きません。keelson dev serve で起動すると本番と同じヘッダーが付き、閲覧のみの利用者と管理権限のある利用者を切り替えて確かめられます。CLI なしで KEELSON_LOCAL_MODE=1 を付けて起動したときは、Identity SDK が固定の 1 人を返します。手順はローカルで動かすを参照してください。

ローカルではカスタムグループを再現できません。グループごとの分岐は、実際の権限を設定したテスト用アプリでも確認してください。

Webhook などログインなしのリクエスト

Section titled “Webhook などログインなしのリクエスト”

外部システムからアプリトークンで呼ばれる /api/external/ や /api/webhooks/ へのリクエストには、ログインユーザーの ID は渡されません。getCurrentUser や getCurrentIdentity で利用者を取得する処理とは分けて実装します。認証方法は外部システムとの連携を参照してください。

ローカルで動かす

アプリを手元で動かすと、Keelson のログインがないため、ユーザー情報のヘッダー(X-Keelson-User-Id など)が付きません。keelson dev serve を使うと、Keelson と同じヘッダーを付けたうえで、利用者を切り替えながら動作を確かめられます。

keelson dev serve 経由KEELSON_LOCAL_MODE=1(CLI なし)
利用者名簿の誰にでも切り替えられる固定の 1 人
ユーザー情報の渡し方本番と同じヘッダーSDK がテスト用データを返す(ヘッダーは付かない)
メンバー一覧・グループ(listMembers など)名簿から返す(Directory API の有効化は不要)名簿から返す
必要なもの最新の Keelson CLI最新の SDK

通常は keelson dev serve を使います。CLI を入れていない、または古い CLI しかないときは KEELSON_LOCAL_MODE=1 で起動します。どちらでも、アプリのコードは同じです。

-- のあとに、アプリのサーバーを起動するコマンドを書きます。

Terminal window
# Node.js
keelson dev serve -- node server.js
# Python
keelson dev serve -- uvicorn main:app --host 127.0.0.1 --port {port}
# Go
keelson dev serve -- go run .
  • CLI がアプリ用の空いているポートを選び、環境変数 PORT で渡します。コマンド中の {port} もその番号に置き換わります。アプリは 127.0.0.1 のそのポートで待ち受けてください
  • ブラウザでは http://localhost:5173/ を開きます。ポートは --port で変えられます
  • CLI はアプリの手前に立ち、リクエストごとに選択中の利用者のヘッダー(X-Keelson-User-Id / X-Keelson-User-Email / X-Keelson-User-Name / X-Keelson-User-App-Perms)を付けます。本番と同じく、ブラウザの Cookie と Authorization ヘッダーはアプリへ届きません
  • 起動すると、アプリの URL、現在の利用者、切り替え方が表示されます。Ctrl-C で止めると、アプリのサーバーも止まります
方法使い方
切り替えページブラウザで http://localhost:5173/__keelson/dev を開き、利用者を選びます。開いている別のタブは、再読み込みするまで前の利用者のままです
X-Keelson-Dev-As ヘッダーリクエストに X-Keelson-Dev-As: <利用者の ID> を付けると、そのリクエストだけその利用者になります。AI エージェントや curl での確認に使います
--askeelson dev serve --as <利用者の ID> -- ... で、起動時の利用者を決めます
Terminal window
# 閲覧権限だけの利用者として API を呼ぶ
curl -H "X-Keelson-Dev-As: local-user-003" http://localhost:5173/api/items

名簿にない ID を指定するとエラーになります。

利用者の一覧(名簿)は JSON ファイルで用意します。次の順で探します。

  • keelson dev serve: --users <ファイル> → ./.keelson/dev-users.json
  • KEELSON_LOCAL_MODE=1: 環境変数 KEELSON_LOCAL_USERS_FILE → ./.keelson/dev-users.json
{
"users": [
{ "id": "sample-tanaka", "email": "[email protected]", "name": "田中 太郎", "perms": ["view", "manage"] },
{ "id": "sample-sato", "email": "[email protected]", "name": "佐藤 花子", "perms": ["view"] }
]
}
項目内容
id利用者の ID。名簿の中で重複させません。本番の ID とは別の、読みやすい文字列でかまいません
email / nameメールアドレスと表示名。空文字も可
perms["view"](閲覧のみ)または ["view", "manage"](管理権限あり)。X-Keelson-User-App-Perms の値になります
image_url任意。プロフィール画像の URL
  • 名簿の先頭で manage を持つ人が、既定の利用者になります
  • 指定したファイルが無い・形が正しくないときは、起動時にエラーになります
  • 名簿ファイルが無いときは、組み込みの 4 人(local-user-001 から local-user-004)を使います。keelson dev serve では、local-user-003 と local-user-004 は閲覧権限だけです
  • 名簿を変えたら、起動し直してください

CLI なしで動かす(KEELSON_LOCAL_MODE=1)

Section titled “CLI なしで動かす(KEELSON_LOCAL_MODE=1)”

アプリのサーバーを、環境変数 KEELSON_LOCAL_MODE=1 を付けて直接起動します。

Terminal window
KEELSON_LOCAL_MODE=1 node server.js

keelson dev serve --users <ファイル> で独自の名簿を使っているアプリは、直接起動するときも KEELSON_LOCAL_USERS_FILE=<同じファイル> を渡してください。CLI の有無で利用者の ID が変わらなくなります。

Identity SDK はヘッダーを読まず、固定の 1 人(名簿の先頭で manage を持つ人)を今の利用者として返します。名簿ファイルが無いときは、管理権限を持つテスト用ユーザー(local-user-001)になります。利用者は切り替えられません。ヘッダーを直接読むコードには、このテスト用データは適用されません。

今の利用者と権限を取得する(getRequestUser)

Section titled “今の利用者と権限を取得する(getRequestUser)”

getRequestUser は、リクエストのヘッダーから今の利用者と、このアプリでの権限(perms)をまとめて返します。外部への通信はしません。本番と、上の 2 つの動かし方のどれでも同じコードで動きます。

import { getRequestUser } from "@keelsonhq/identity";
app.get("/admin", async (req, res) => {
const user = await getRequestUser({ headers: req.headers }); // { id, email, name, perms }
if (!user.perms.includes("manage")) return res.status(403).end();
res.send(`${user.name ?? user.id} さん、こんにちは`);
});
from keelson import identity
user = identity.get_request_user(headers=request.headers)
if "manage" not in user.perms:
... # 403 を返す
client, err := identity.New("") // 起動時に 1 回
// ハンドラの中で
user, err := client.GetRequestUser(identity.WithHeaders(r.Header))
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
canManage := slices.Contains(user.Perms, "manage")

ユーザー ID のヘッダーが無いときはエラーになります。perms は X-Keelson-User-App-Perms を分けたもので、ヘッダーが無ければ空です。日本語の表示名も正しく読み取れます。

ローカル開発の仕組みは、Keelson にデプロイしたアプリでは動きません。

  • デプロイ先で KEELSON_LOCAL_MODE が設定されていると、SDK はテスト用データを返さずにエラーにします。KEELSON_LOCAL_MODE を外してください
  • keelson dev serve も、デプロイ先の環境では起動しません

AI クライアントから使う(MCP)

アプリを MCP サーバー(AI クライアントからアプリを使う機能)にできます。MCP(Model Context Protocol)は、Claude や ChatGPT などの AI クライアントが、外部の「ツール」を一覧して呼び出すための取り決めです。たとえば在庫管理アプリに「品目を検索する」「入荷を記録する」ツールを用意すると、利用者は AI クライアントとの会話の中から、自分の権限の範囲でアプリを操作できます。

アプリ側で用意するのは、keelson.yaml の mcp: でのツールの宣言と、ツールごとの JSON の受け口(ハンドラ)だけです。ログイン、同意画面、AI クライアントとの MCP のやり取りは Keelson が受け持ちます。

  1. アプリ作者が keelson.yaml の mcp: にツールを宣言してデプロイします
  2. 利用者が AI クライアントに接続 URL(アプリの URL + /mcp)を登録します
  3. 初回は Keelson のログインと同意画面が出ます。利用者が承認すると、AI クライアントがその利用者として接続されます
  4. AI クライアントがツールを呼ぶと、Keelson がメンバーであること・アプリの権限・IP 制限を確かめ、引数を宣言どおりか検査します
  5. Keelson がアプリの POST /api/mcp/<ツール名> を呼び、アプリが返した JSON をツールの結果として AI クライアントに返します

使えるのは、サーバー側のプログラムが動くアプリ(command があり assets がないアプリ)だけです。静的サイト・SPA・ハイブリッド構成では宣言できません。全プランで使え、プランによる違いはありません。

keelson.yaml に mcp: を書いてデプロイします。次の例は、読み取りのツール search_items と、書き込みのツール create_arrival を宣言しています。

slug: stock-desk
runtime: python-slim
command: "python app.py"
db:
mode: libsql
mcp:
enabled: true
instructions: "Look up stock levels and record incoming deliveries."
tools:
- name: search_items
description: "Search stock by item name."
access: read
input:
type: object
properties:
query:
type: string
required: [query]
- name: create_arrival
description: "Record one incoming delivery."
access: write
permission: manage
input:
type: object
properties:
item_id:
type: string
quantity:
type: integer
minimum: 1
required: [item_id, quantity]
キー意味
enabledtrue でツールを公開します。false にすると宣言を残したまま呼び出しを止めます
instructionsAI クライアントに渡す、ツール全体の説明(省略可)
tools[].nameツール名。英小文字で始まり、英小文字・数字・_ だけ。アプリの受け口は POST /api/mcp/<name> になります
tools[].descriptionAI クライアントに見せるツールの説明。AI はこれを読んで、いつ呼ぶかを決めます
tools[].accessread(読み取り)か write(データを変更する)。読み取りと書き込み・権限を参照
tools[].permissionview(既定)か manage。manage のツールはアプリの管理権限を持つ人だけが使えます
tools[].input引数の形(JSON Schema 2020-12)。最上位は type: object

各キーの型・上限・検査の詳細は keelson.yaml リファレンスを参照してください。

ツールが多いときは別ファイルに分ける(mcp.file)

Section titled “ツールが多いときは別ファイルに分ける(mcp.file)”

mcp.file を使うと、instructions と tools を別の YAML ファイルに置けます。keelson.yaml には enabled と file だけを書きます。

slug: stock-desk
runtime: python-slim
command: "python app.py"
db:
mode: libsql
mcp:
enabled: true
file: mcp.yaml

keelson.yaml と同じフォルダの mcp.yaml:

instructions: "Look up stock levels and record incoming deliveries."
tools:
- name: search_items
description: "Search stock by item name."
access: read
input:
type: object
properties:
query:
type: string
required: [query]
- name: create_arrival
description: "Record one incoming delivery."
access: write
permission: manage
input:
type: object
properties:
item_id:
type: string
quantity:
type: integer
minimum: 1
required: [item_id, quantity]
  • デプロイ時に Keelson CLI が mcp.yaml の中身を keelson.yaml に差し込んでから検査・送信します。手元のファイルは書き換えません
  • 別ファイルに書けるのは instructions と tools だけです。mcp.file と mcp.instructions / mcp.tools は同時に書けません
  • mcp.file を読めるのは新しい Keelson CLI だけです。古い CLI は「未対応のキー」としてデプロイを拒否します。その場合は keelson upgrade で CLI を最新版にしてください

ツールが呼ばれると、Keelson がアプリに POST /api/mcp/<ツール名> を送ります。パスはツール名で決まり、変更できません。

本文は、AI クライアントが渡した引数の JSON オブジェクトです(引数がなければ {})。宣言した input で検査済みのものだけが届きます。

ヘッダーは Keelson が組み立てたものだけが届きます。AI クライアントが送ったヘッダーは 1 つも引き継がれないため、AI クライアントのアクセストークンや、偽造された X-Keelson-* ヘッダーはアプリに届きません。

ヘッダー内容
X-Keelson-User-Id接続した利用者のユーザー ID。本人の識別にはこれを使います
X-Keelson-User-Email同じ利用者のメールアドレス
X-Keelson-User-Name同じ利用者の表示名
X-Keelson-User-App-Permsその利用者のアプリの権限(view または view,manage)
X-Keelson-Auth-Sourcemcp(MCP 経由の呼び出しであることを示す)
X-Keelson-Mcp-Tool呼ばれたツール名
X-Keelson-Mcp-ClientAI クライアントの識別子(URL)
X-Request-Idリクエスト ID。エラーの結果にも request id: として表示されます

Authorization ヘッダーには Keelson の内部で使う値が入ります。AI クライアントのトークンではないため、アプリでは使いません。ユーザー情報ヘッダーの使い方はログイン中のユーザー情報を使うと同じです。

応答は 2xx の JSON(1 MiB まで)で返します。JSON オブジェクトを返すと、AI クライアントが項目を読み取りやすくなります。

アプリの応答AI クライアントに返る結果
2xx と JSON の本文ツールの結果として本文の JSON を返す
2xx で本文が空・JSON でない・1 MiB を超えるエラー(ツールの応答が不正)
4xxエラー。本文の先頭 4 KiB を利用者と AI クライアントにそのまま見せる
3xx・5xxエラー(ツールの実行に失敗)。本文は見せない
60 秒以内に応答がないエラー(実行されたか分からない)
  • 4xx の本文は利用者と AI クライアントに見えます。 見せてよい説明だけを書きます(例:「品目 A-100 が見つかりません」)
  • 1 回の呼び出しは全体で 60 秒までです。時間がかかる処理はバックグラウンドタスクに投入して、すぐに応答を返します
  • Keelson は失敗した呼び出しを再試行しません
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/api/mcp/search_items")
async def search_items(request: Request):
user_id = request.headers["X-Keelson-User-Id"]
args = await request.json() # 例: {"query": "ねじ"}
items = find_items(owner=user_id, query=args["query"]) # アプリの検索処理
return {"items": items}
@app.post("/api/mcp/create_arrival")
async def create_arrival(request: Request):
perms = request.headers.get("X-Keelson-User-App-Perms", "").split(",")
if "manage" not in perms:
return JSONResponse({"error": "入荷を記録する権限がありません"}, status_code=403)
args = await request.json()
if not item_exists(args["item_id"]):
# 4xx の本文は利用者に見える
return JSONResponse({"error": f"品目 {args['item_id']} が見つかりません"}, status_code=404)
arrival_id = record_arrival(args["item_id"], args["quantity"])
return {"arrival_id": arrival_id}
app.use(express.json());
app.post("/api/mcp/search_items", (req, res) => {
const userId = req.headers["x-keelson-user-id"];
const items = findItems({ owner: userId, query: req.body.query }); // アプリの検索処理
res.json({ items });
});
app.post("/api/mcp/create_arrival", (req, res) => {
const perms = (req.headers["x-keelson-user-app-perms"] ?? "").split(",");
if (!perms.includes("manage")) {
return res.status(403).json({ error: "入荷を記録する権限がありません" });
}
const { item_id, quantity } = req.body;
if (!itemExists(item_id)) {
// 4xx の本文は利用者に見える
return res.status(404).json({ error: `品目 ${item_id} が見つかりません` });
}
res.json({ arrival_id: recordArrival(item_id, quantity) });
});

permission: manage のツールは、管理権限を持たない人からは Keelson が呼び出しを通しません。上の例のようにアプリ側でも X-Keelson-User-App-Perms を確かめておくと、宣言を変えたときにも安全です。

  • access: write のツールは、同意画面で「書き込みツール(データを変更できる)」として別に表示されます。 利用者は「読み取りツールだけ承認」を選べます。その場合、書き込みツールは使えません。後から使うには、接続し直して承認します
  • permission: manage のツールは、アプリの管理権限(manage)を持つ人だけが使えます。 権限の割り当てはメンバーとロールを参照してください
  • 使えないツールは一覧に出ません。 権限が足りないツールと、同意していない書き込みツールは、AI クライアントのツール一覧から除かれます
  • 呼び出しはすべて、接続した利用者本人として、その人の権限の範囲で行われます。メンバーから外れた人やアプリの閲覧権限を失った人の呼び出しは、通常 2 分以内に拒否されるようになります

利用者は、AI クライアントに接続 URL を登録します。接続 URL はアプリの URL の末尾に /mcp を付けたもの(例: https://acme--stock-desk.keelson.run/mcp)で、コンソールのアプリの MCP タブにも表示されます。接続 URL の登録方法は、各 AI クライアントの MCP 設定のドキュメントを参照してください。

  1. AI クライアントに接続 URL を登録します
  2. 初回は Keelson のログイン画面が開きます。アプリを使えるアカウントでログインします
  3. 同意画面で、AI クライアントに使わせるツールを確認して 承認 を選びます
  • 接続できるのは、そのアプリを閲覧できるメンバーだけです
  • 受け付ける AI クライアントは、claude.ai または chatgpt.com の身元を名乗るものだけです。それ以外のクライアントは、接続 URL を登録してもログインの段階で止まります
  • アクセストークンの有効期間は 1 時間で、AI クライアントが自動で更新します。更新用のトークンは、60 日使われないか、接続から最長 180 日で切れます。切れたら接続し直します
  • 同じ AI クライアントで同じアプリに接続し直すと、前の接続は解除されます
操作できる人効果
ワークスペースの MCP を停止(設定 → セキュリティ → MCP(AI クライアント))Owner / Adminワークスペースのすべてのアプリで呼び出しを止める。接続は残り、許可に戻すとそのまま使える
mcp.enabled: false にしてデプロイアプリをデプロイできる人そのアプリの呼び出しを止める。接続は残り、true に戻してデプロイするとそのまま使える
アプリの MCP タブの「接続中の AI クライアント」で 失効Owner / Admin、アプリの管理権限を持つ人その接続を解除する。AI クライアントは更新用のトークンも含めて直ちに使えなくなる
「自分の接続中の AI クライアント」で 失効接続した本人自分の接続を解除する

停止と mcp.enabled: false は呼び出しを止めるだけです。接続そのものを失効させるのは、解除(失効)だけです。 「自分の接続中の AI クライアント」は、設定 → セキュリティ と同意画面からリンクしています。

  • mcp.enabled: true のアプリでは、/mcp、/.well-known/oauth-protected-resource、/.well-known/oauth-protected-resource/mcp は Keelson が応答し、アプリには届きません
  • サーバー側のプログラムが動くアプリ(ハイブリッド構成のサーバー側を含む)では、/api/mcp と /api/mcp/ で始まるパスは、mcp: を宣言していなくても MCP 経由以外では 404 になります。このパスにはツールのハンドラ以外のルートを置かないでください。静的に配信される部分には影響しません

予約パスの一覧は Keelson Deploy Spec の予約 URL パスを参照してください。

項目上限
ツールの数1 アプリ 40 本
instructions4096 文字
ツールの description1024 文字
ツールの inputJSON にして 64 KB・入れ子 32 段
1 回の呼び出し60 秒
応答の本文(2xx)1 MiB
呼び出し回数1 接続あたり 1 分 60 回
同時に実行される呼び出し1 アプリ 10(Keelson の中継サーバー 1 台ごとの値。中継サーバーが増えるとアプリ全体ではこれより多く同時に届くことがあります)

回数と同時実行の上限を超えた呼び出しは、少し待ってから再試行するよう AI クライアントに返されます。上限はプランで変わりません。同時実行の 10 はアプリが受ける同時呼び出しの最大値ではないので、データベースの接続数などを 10 件前提で決めないでください。

症状確認すること
ツールが一覧に出ないその利用者にアプリの権限があるか(permission: manage のツールは管理権限が必要)。書き込みツールなら、同意画面で承認したか。mcp.enabled が true か。ワークスペースの MCP が停止されていないか(アプリの MCP タブの状態に表示されます)
IP 制限のあるワークスペースで呼び出しが拒否されるIP 制限は MCP 経由の呼び出しにも適用されます。claude.ai や ChatGPT からの呼び出しは利用者の PC ではなく各社のサーバーから届くため、そのままでは許可されません。IP 制御のアクセス元を見直してください
「ツールの応答が不正」と表示されるハンドラが 2xx で JSON の本文を返しているか。本文が空・テキスト・1 MiB 超になっていないか
ログインの後に接続できないAI クライアントが claude.ai または chatgpt.com の身元を名乗るものか。ログインしたアカウントがアプリを閲覧できるメンバーか
デプロイで mcp の file が未対応のキーとして拒否されるKeelson CLI が古いため。keelson upgrade で最新版にしてからデプロイし直します

外部システムとの連携

OpenAI などの API を呼ぶ、外部サービスの通知を受け取る、外部データベースに接続するなど、用途に応じて連携方法を選べます。

やりたいこと必要な対応
外部の API を呼ぶ接続情報や API キーを設定する
アプリの利用者にメールを送るEmail SDK で送る
外部サービスから通知を受けるWebhook 用のアプリトークンと受信先を設定する
社内の別システムからアプリの API を呼ぶアプリトークンと呼び出せるパスを設定する
外部 DB やストレージを使う接続情報を設定する

アプリから外部サービスへの通信に制限はありません。例外は SMTP のポート(TCP 25・465・587・2525)で、これらは常に遮断されます。必要な API キーをシークレットに設定し、アプリから呼び出します。外部サービス側の契約や呼び出し回数の制限は、そのサービスの条件に従います。

メールを送信するには、Keelson の Email SDK か、Resend・SendGrid・Mailgun など契約したメール送信サービスの HTTPS API を使います。契約したサービスでも SMTP 中継ホストは使えないため、SMTP のホストとポートではなく API クライアントを設定してください。

一方、外部のシステムからアプリを呼ぶ場合は、既定で Keelson のログインが必要です。ブラウザでログインできないシステムから呼び出すには、以下の設定を行います。

Webhook は、外部サービスで起きた出来事をアプリに通知する仕組みです。たとえば決済完了の通知を受けて、注文の状態を更新できます。

Webhook の受信には、webhook スコープのアプリトークンが必要です。送信元の署名がある場合も、このトークンを併用します。auth.endpoints を設定するだけでは、署名のみのリクエストを受け取れるようにはなりません。

まず、対象アプリのトークンを発行します。my-app はアプリの slug に置き換えてください。

Terminal window
keelson apps tokens create --app my-app --name webhook-client --scope webhook

アプリに POST /api/webhooks/incoming などの受信処理を実装します。次のパスをアプリの URL(例:https://acme--my-app.keelson.run)に付け、送信元の受信先として登録します。

送信元で設定できるもの設定方法
任意の HTTP ヘッダー受信 URL を /api/webhooks/incoming にし、X-Webhook-Secret: <発行したトークン> を付ける
受信 URL のみ受信 URL を /api/webhooks/<発行したトークン>/incoming にする

URL にトークンを含めた場合、Keelson はトークン部分を取り除き、アプリの /api/webhooks/incoming に渡します。トークンを含む URL は秘密の値として扱ってください。これらのリクエストにログインユーザーの情報は付きません。

Keelson はトークンの有効性、対象アプリ、webhook スコープ、送信元 IP の制限を確認します。現在、Webhook のパスやメソッドを auth.endpoints で制限することはできません。受信処理で許可するパスとメソッドを確認してください。

送信元が署名を付ける場合は、署名シークレットを secrets で受け取り、アプリ側でも署名を検証します。Keelson のアプリトークンは、送信元の署名シークレットとは別のものです。

なお /api/webhooks/email と /api/webhooks/email-events はプラットフォーム予約のため、独自の Webhook の受信先には使えません。auth.endpoints には追加しないでください。

別のシステムからアプリの API を呼ぶ

Section titled “別のシステムからアプリの API を呼ぶ”

社内の別システムやスクリプトからアプリの API を呼ぶ場合は、アプリトークンを発行します。

Terminal window
keelson apps tokens create --app my-app --name batch-client --scope api --allowed-ip 203.0.113.0/24

my-app は対象アプリの slug、203.0.113.0/24 は実際の送信元 IP の範囲に置き換えます。

  • トークンは keelson_... の形式で、作成時に 1 回だけ表示されます
  • --scope api は /api/external/ 配下、--scope webhook は /api/webhooks/ 配下に対応します
  • --allowed-ip で送信元 IP を絞れます(CIDR、複数可)。ワークスペースやアプリの IP 制御とは別に、トークン単位で適用されます
  • 同じ設定はコンソールのアプリ画面からも行えます。CLI の keelson apps tokens 配下には、一覧を表示する list、トークンを更新する rotate、失効させる delete があります

呼び出し側は Authorization: Bearer keelson_... を付けて、auth.endpoints で宣言したパスを呼びます。

Terminal window
curl -H "Authorization: Bearer keelson_xxx" https://acme--my-app.keelson.run/api/external/status
auth:
endpoints:
- path: /api/external/status
methods: [GET]
- /api/external/import # メソッド指定なし = すべて

Keelson は、トークンが有効で対象アプリ・用途に合っていること、送信元 IP が許可されていること、パスとメソッドが宣言に合っていることを確認してから、アプリにリクエストを渡します。

auth.endpoints のパスは完全一致ではなく、先頭の文字列で照合されます。たとえば /api/external/status は /api/external/status/detail や /api/external/status-old にも一致します。アプリ側でも受け付けるパスとメソッドを確認してください。宣言していない API は、api スコープのトークンでは呼び出せません。

Keelson の Email SDK を使うと、メール送信サービスを契約せずに、アプリからアプリの利用者へメールを送れます。パスワード再設定、申請の受付通知、承認依頼などの業務連絡に使います。

送信できるのは、そのアプリの利用者への業務上の連絡だけです(利用ポリシー 2.3)。マーケティングの一斉送信、購入した宛先リストへの送信、アプリを使っていない人への送信はできません。継続的に送る通知には、受信者が受信を停止できる手段を設けてください。

import * as email from "@keelsonhq/email";
const result = await email.send({
subject: "申請を受け付けました",
text: "申請番号 1234 を受け付けました。",
});
// result.send_id を保存しておくと、配信結果と突き合わせられます

Python では from keelson import email で読み込み、email.send(...) を呼びます。Go では github.com/keelsonhq/go-sdk/email の email.New("", "") でクライアントを作り、Send を呼びます。インストールは Keelson SDK を参照してください。

  • 接続先とトークン(KEELSON_EMAIL_TOKEN など)は、Keelson がデプロイ時に自動で設定し、SDK が読み取ります。keelson.yaml への追加設定は不要です。静的サイト・SPA には SDK を動かすサーバーがないため送れません
  • 差出人は、既定では <アプリの slug>@mail.keelson.run です。表示名(from_name)と返信先(reply_to)は指定できます。Team プランでは、自社のドメインの許可したアドレスも差出人にできます(下記)
  • 1 通の宛先は To・CC・BCC の合計で 50 件までです。本文には text か html の少なくとも一方が必要です
  • 送信の回数は、アプリごと・ワークスペースごとにそれぞれ 60 秒あたり 30 回までです
  • 月ごとに送れる宛先数は、プランごとに決まっています(プランと制限)。当月の使用量は、コンソールのワークスペース設定の「メール」で確認できます

Team プランでは、ワークスペースが所有するドメインのアドレス(例 [email protected])を差出人にして送れます。ドメインはワークスペースあたり 10 件まで、差出人に使える許可アドレスはドメインあたり 20 件まで登録できます。設定は Owner / Admin が、コンソールのワークスペース設定の「メール」>「送信ドメイン」で行います。ドメインの DNS 設定を変更できる権限が必要です。

  1. 「ドメインを追加」でドメインを入力します。1 つのアプリだけに使わせる場合は「スコープ」でアプリを選びます。選ばなければ、ワークスペースのすべてのアプリが使えます。
  2. 「所有の確認」に表示された TXT レコードを DNS に追加し、「状態を確認」を押します。1 つのドメインを所有確認できるのは 1 ワークスペースだけです。
  3. 所有が確認されると、メールの署名(DKIM)用の CNAME レコードが 3 件表示されます。DNS に追加してください。検証は通常数分で終わります。状態は「状態を確認」を押すと確認できます。72 時間以内にレコードが見つからない場合は検証に失敗します。ドメインを削除して追加し直してください。
  4. 「許可アドレス」に、アプリが差出人に使うアドレスを追加します。
  5. アプリでは、そのアドレスを from_address(Node・Python)または From(Go)に渡します。
await email.send({
from_address: "[email protected]",
from_name: "Example サポート",
subject: "申請を受け付けました",
text: "申請番号 1234 を受け付けました。",
});
  • 差出人は送信のたびに確認されます。送信するアプリのワークスペースで検証済みのドメインの許可アドレスと完全に一致しない差出人(大文字・小文字は区別しません)は、送信を拒否します。許可アドレスを削除すると、次の送信から拒否されます
  • メールには自社のドメインの DKIM 署名が付き、DMARC の判定で差出人のドメインと整合します。返送先(SPF)には自社のドメインを使わないため、SPF は整合しません
  • 既定の差出人 <アプリの slug>@mail.keelson.run は、許可アドレスの登録なしでそのまま使えます
  • ワークスペースが Team プランでなくなると、自社のドメインからの送信は拒否されます。登録したドメインと許可アドレスは残り、Team に戻すと再び使えます
  • バウンス・苦情と抑止リストは、既定の差出人と同じように働きます

送信できない場合、SDK はエラーを送出します。エラーメッセージにエラーコードが含まれます。

HTTPエラーコード意味
400RECIPIENT_SUPPRESSED宛先が、ワークスペースの抑止リストに載っています(下記)
403EMAIL_SENDING_SUSPENDEDワークスペースのメール送信が停止されています。恒久的なバウンスや苦情が多いと、ワークスペース全体の送信が自動で停止します。解除はお問い合わせください
403SENDER_DOMAIN_NOT_AVAILABLE差出人が自社のドメインですが、ワークスペースが Team プランではありません
403DOMAIN_NOT_VERIFIED差出人のドメインが、送信するアプリのワークスペースで検証済みでないか、別のアプリに限定されています
403SENDER_ADDRESS_NOT_ALLOWED差出人が、そのドメインの許可アドレスに登録されていません
422RECIPIENT_LIMIT_EXCEEDED宛先が合計 50 件を超えています
429RATE_LIMIT_EXCEEDED60 秒あたりの送信回数の上限に達しました。時間をおいて送り直します
429MONTHLY_QUOTA_EXCEEDED当月の宛先数の上限に達しました。翌月にリセットされます
429GLOBAL_RATE_LIMIT_EXCEEDED / GLOBAL_DAILY_QUOTA_EXCEEDEDKeelson 全体の送信量が一時的に上限に達しました。時間をおいて送り直します
502SEND_OUTCOME_UNKNOWN送信されたかを確認できませんでした。届いている可能性があるため、自動では送り直さないでください

送ったメールが届いたか、宛先が存在しないなどの理由で返ってきたか(バウンス)、受信者が迷惑メールとして報告したか(苦情)を、Keelson がアプリの POST /api/webhooks/email-events に通知します。結果を使う場合は、このパスに受信処理を実装します。アプリトークンや auth.endpoints の設定は不要です。

恒久的なバウンスと苦情を受けた宛先は、受信処理の有無にかかわらず、ワークスペースの抑止リストに自動で載ります。以後、同じワークスペースのどのアプリからもその宛先には送れません(RECIPIENT_SUPPRESSED)。抑止リストは、Owner / Admin がコンソールのワークスペース設定の「メール」で確認・削除できます。

通知には署名が付いています。SDK で署名を検証してから内容を使います。署名の鍵 KEELSON_EMAIL_WEBHOOK_SECRET は Keelson が自動で設定します。

import * as email from "@keelsonhq/email";
// POST /api/webhooks/email-events の受信処理。rawBody は受け取った本文そのもの
const event = email.verifyEventWebhookBytes(
rawBody,
req.headers,
process.env.KEELSON_EMAIL_WEBHOOK_SECRET!,
);
if (event.event_type === "bounce" && event.bounce_type === "hard") {
// event.send_id で送信時の記録を探し、宛先を無効にする
}

Python では email.verify_event_webhook(body, headers, secret)、Go では email.VerifyEventWebhook(r, secret) を使います。署名は本文のバイト列に対して検証するため、JSON として解析し直す前の本文を渡してください。

項目内容
event_id通知の ID。同じ通知が重複して届くことがあるため、重複の判定に使います
event_typedelivered(届いた)・bounce(バウンス)・complaint(苦情)
email_address対象の宛先
send_id送信時に返された send_id。送信の記録との突き合わせに使います
bounce_typeバウンスのとき、hard(恒久的)か soft(一時的)
detail補足の情報
provider配信に使った送信基盤の名前
timestamp発生時刻

受信処理が 2xx を返すと通知は完了します。5xx を返したときやアプリに届かなかったときは、時間をおいて合計 4 回まで試みます。4xx を返した通知は送り直しません。

PostgreSQL、MySQL、自前の libSQL に接続する場合は db.mode: none にし、接続情報をシークレットで渡します。詳しくはデータベースを参照してください。

Files / Media SDK の上限を超えるファイルは、S3 互換のストレージにアプリから直接保存します。認証情報はシークレットで渡します。

メールを受け取る

アプリに専用のメールアドレスを持たせることができます。届いたメールは、Keelson がアプリのデータベースの表に 1 通 1 行で保存し、添付ファイルを Files に置きます。アプリは SQL で読むだけで、メールを受け取るコードは要りません。保存したメールを 1 通ずつ処理したい場合は、on_receive にバックグラウンドタスクの名前を書きます。

主な用途は、問い合わせの共有受信箱や、注文・請求のメールの読み取りです。独自ドメインのメールを Keelson で直接受けることはできません。Gmail・Google Workspace・Microsoft 365 から、アプリのアドレスへ転送します(アプリへ転送する)。

通常は AI エージェントに、届いたメールをどう扱うかを伝えると、keelson.yaml の設定と処理のタスクを書きます。

営業グループ(sales@)から転送される注文メールを保存して、1 通ごとに注文として登録してください。同じメールを 2 回処理しても、注文が重複しないようにしてください。

keelson.yaml に email.inbound を書いてデプロイします。受信には Managed SQLite(db.mode: libsql)が必要です。

slug: order-inbox
runtime: python-slim
command: "uvicorn main:app --host 0.0.0.0 --port $PORT"
db:
mode: libsql # 必須
email:
inbound:
enabled: true
on_receive: process-mail # 任意。tasks に宣言したタスク
tasks:
- name: process-mail
command: "python process_mail.py"
項目意味
email.inbound.enabledtrue で受信を有効にする。デプロイで受信アドレスを発行し、表 keelson_inbound_emails を作る
email.inbound.on_receive任意。tasks に宣言したタスクの名前。メールを保存するたびに、このタスクを起動する

次の場合はデプロイが拒否されます。

エラー原因
email_inbound_requires_libsqldb.mode: libsql でないのに enabled: true にした
email_on_receive_requires_enabledon_receive があるのに enabled が true でない
email_on_receive_unknown_taskon_receive の名前が tasks[].name のどれとも一致しない

on_receive を書いたアプリは、tasks があっても command と crons が無くて構いません(受信がタスクを起動するため)。添付ファイルの置き場は固定で、設定できません。

受信アドレスは、コンソールのアプリの Email 設定に表示されます。形は in+<ランダムな文字列>@inbound.keelson.run です。ランダムな部分はアプリの slug と関係が無く、アプリ名を変えてもアドレスは変わりません。

  • 作り直す:コンソールから新しいアドレスを発行できます。古いアドレスはその場で退役し、以後そのアドレス宛てのメールは拒否されます。
  • 受信をやめる:email.inbound を外した(または enabled: false にした)デプロイで、アドレスは退役し、二度と使えなくなります。まだ保存していないメールと、保存できずに保留しているメールは破棄します。保存済みの行と添付ファイルはそのまま残ります。
  • もう一度有効にする:新しいアドレスを発行します。転送の設定を新しいアドレスに直してください。

退役したアドレスは、どのアプリでも再利用しません。

1 通のメールが keelson_inbound_emails の 1 行になります。表はアプリ自身のデータベースにあるので、他の表と同じ接続で読めます。

列内容
email_id主キー。受け取ったときに決まる
schema_version行を書いたときの列の版。今は 1
received_at受け取った時刻(ISO 8601、UTC)
envelope_to配送されたアプリの受信アドレス
envelope_from配送に使われた送信元アドレス(エンベロープの送信元)
from_address差出人のアドレス。X-Original-From があればそれ、無ければ From。認証済みの値ではない(後述)
from_name差出人の表示名(同じヘッダーから)
header_from生の From ヘッダー(転送グループが書き換えた後の値)
reply_toReply-To ヘッダー
to_addressesTo のアドレスの JSON 配列(転送されたメールでは共有受信箱のアドレス)
cc_addressesCc のアドレスの JSON 配列
subject件名
message_idMessage-ID ヘッダー
in_reply_toIn-Reply-To ヘッダー
references_headerReferences ヘッダー
list_idList-Id ヘッダー
text_bodyテキストの本文(UTF-8 に変換済み)
html_bodyHTML の本文(UTF-8 に変換済み)
body_truncatedどちらかの本文が 1 MiB を超えて切られたら 1
attachments添付ファイルの JSON 配列(無ければ [])
spam_score簡易的な spam の採点。印だけ(spam)
auth_spf / auth_dkim / auth_dmarc受信サーバーが記録した SPF / DKIM / DMARC の結果

無いヘッダーは NULL です。符号化された件名やファイル名(日本語を含む)は復号して保存します。

to_addresses と cc_addresses はアドレスの配列です(表示名が無ければ name は null)。

[{"address": "[email protected]", "name": "サポート窓口"}]

attachments の各要素は次の形です。

[{"attachment_id": "att_01", "filename": "注文書.pdf", "content_type": "application/pdf",
"content_id": null, "size_bytes": 182344, "files_key": "inbound/<email_id>/att_01",
"sha256": "<hex>"}]

content_id は、HTML の本文から参照されるインライン画像に入ります。

-- 新しい順に 20 通
SELECT email_id, received_at, from_address, subject
FROM keelson_inbound_emails
ORDER BY received_at DESC
LIMIT 20;
-- 1 通の添付ファイル
SELECT json_extract(a.value, '$.filename') AS filename,
json_extract(a.value, '$.files_key') AS files_key
FROM keelson_inbound_emails e, json_each(e.attachments) a
WHERE e.email_id = ?;

添付ファイルは Files のキー inbound/<email_id>/<attachment_id> に置かれます。attachments の files_key がこのキーです。Files SDK で読みます。

import json
from keelson import files
(attachments,) = conn.execute(
"SELECT attachments FROM keelson_inbound_emails WHERE email_id = ?", (email_id,)
).fetchone()
for att in json.loads(attachments):
data = files.read(att["files_key"]) # bytes | None
import * as files from "@keelsonhq/files";
const { rows } = await db.execute({
sql: "SELECT attachments FROM keelson_inbound_emails WHERE email_id = ?",
args: [emailId],
});
for (const att of JSON.parse(rows[0].attachments)) {
const data = await files.read(att.files_key); // Uint8Array | null
}

添付ファイルは Files の書き込み上限(10 MiB)を超えることがありますが、読み取りはできます。添付ファイルはワークスペースのストレージに数えます。表の行は数えません。

  • keelson_inbound_emails は、受信を有効にしたデプロイで Keelson が作ります。移行やアプリのコードで自分で作らないでください。Keelson が作っていない同名の表が既にあると、デプロイが止まります。
  • 表は利用者のものです。アプリから読み書き・削除できます。「対応済み」「担当者」などの状態は、email_id をキーにした別の表に持ちます。
  • Keelson は行を足すだけで、既存の行の書き換えや表の作り直しはしません。
  • email_id の主キーを外す、列を消す、トリガーを付ける、Files のキー inbound/ の下に自分のファイルを置く、のいずれかをすると、新しいメールを保存できず保留になります(保存できなかったメール)。
  • データベースをバックアップから復元すると、この表もその時点の中身に戻ります。

on_receive にタスクを書くと、メールを保存するたびに Keelson がそのタスクを起動します。タスクの payload はメールの ID だけです。

{"attempt_no":1,"payload":{"email_id":"…"},"schema_version":1,"task_id":"…","task_name":"process-mail"}

タスクは email_id で keelson_inbound_emails の行を読みます。起動のされ方以外は、通常のバックグラウンドタスクと同じです(バックグラウンドタスク)。

タスクは少なくとも 1 回起動します。 同じ email_id が 2 回以上渡ることがあり、失敗した試行は再試行されます。処理済みのメールを記録し、その記録と処理の結果を 1 つのトランザクションで書きます。

import json
import os
import sys
import libsql
email_id = json.loads(sys.stdin.readline())["payload"]["email_id"]
conn = libsql.connect(
database=os.environ.get("KEELSON_DB_URL", "file:local.db"),
auth_token=os.environ.get("KEELSON_DB_AUTH_TOKEN", ""),
)
conn.execute(
"CREATE TABLE IF NOT EXISTS handled_emails (email_id TEXT PRIMARY KEY, handled_at TEXT NOT NULL)"
)
cur = conn.execute(
"INSERT INTO handled_emails (email_id, handled_at) VALUES (?, datetime('now')) "
"ON CONFLICT (email_id) DO NOTHING",
(email_id,),
)
if cur.rowcount == 0:
sys.exit(0) # 処理済み
(subject,) = conn.execute(
"SELECT subject FROM keelson_inbound_emails WHERE email_id = ?",
(email_id,),
).fetchone()
conn.execute("INSERT INTO orders (email_id, title) VALUES (?, ?)", (email_id, subject))
conn.commit() # 処理済みの記録と注文を一緒に保存する

commit の前にタスクが失敗すると何も保存されず、再試行で最初からやり直します。外部 API には email_id を冪等キーとして渡します。

  • 保存済みのメールの on_receive は、コンソールから起動し直せます。数え方は自動の起動と同じです。
  • on_receive で起動したタスクは、バックグラウンド実行の回数に数えません。実行時間は月間の合計実行時間に合算します。合計実行時間の枠を使い切っているとタスクは起動せず、コンソールにその旨が出ます(自動では再試行しません)。
  • 処理が重いと合計実行時間を使い切り、定期実行ジョブも止まります。コンソールで月間の使用量を確認してください。

from_address は X-Original-From か From のヘッダーから取ります。このヘッダーには誰でも好きなアドレスを書けます。from_address を、利用者の本人確認や、そのメールに何を許すかの判断に使わないでください。(例:「社長から来たメールなら支払いを承認する」)。表示と振り分けにだけ使います。

auth_spf・auth_dkim・auth_dmarc は受信サーバーの検査の記録です。転送で崩れることが多く、これも差出人の証明にはなりません。

独自ドメインのメールを Keelson で直接受けることはできません。今メールを受けている所で受けたまま、アプリのアドレスへ転送します。

  1. Gmail の 設定 → すべての設定を表示 → メール転送と POP/IMAP で、アプリの受信アドレスを転送先に追加します。
  2. Gmail が [email protected] から確認メールをそのアドレスへ送ります。コンソールのアプリの Email 設定の 最近届いたメール で確認コードを読みます。アプリが動いていなくても読めます。
  3. Gmail に確認コードを入力し、転送を有効にします。一部のメールだけを転送する場合は、Gmail のフィルタで転送します。

確認メールも、他のメールと同じように表に保存されます。on_receive を使っている場合は、確認メールでもタスクが起動します。

メールを受けている Google グループ(例:support@)のメンバーに、アプリの受信アドレスを追加します。グループに届いたメールの写しがアプリにも届きます。

  • アドレスを追加できない場合は、Google Workspace の管理者が、組織外のメンバーをグループに追加できるように設定する必要があります。
  • 元の差出人のドメインの DMARC ポリシーが厳しいと、Google グループは From をグループのアドレスに書き換え、元の差出人を X-Original-From に残します。from_address は X-Original-From を使うので、元の差出人が入ります。header_from には書き換え後の From、to_addresses にはグループのアドレスが入ります。

Microsoft 365 は、外部のアドレスへの自動転送を既定で止めています(送信の迷惑メールフィルターのポリシー)。転送する前に、管理者が許可する必要があります。許可していないと、転送したメールは 550 5.7.520 Access denied, Your organization does not allow external forwarding で戻ってきます。

  1. Microsoft Defender ポータルで、送信の迷惑メール対策ポリシーの自動転送をオンにします。転送するメールボックスだけに適用する独自のポリシーを作るのが安全です。
  2. そのメールボックスで、アプリのアドレスへの転送(Outlook の転送設定か受信トレイのルール)を設定します。メールフローのルールでも転送できます。

Microsoft 365 からの転送は Keelson では試していません。手順は Microsoft の文書に基づきます。

項目上限
1 通の大きさ添付ファイルを含めて 25 MiB。超えるメールは拒否される
本文text_body・html_body はそれぞれ 1 MiB で切る(body_truncated = 1)
添付ファイル25 MiB の範囲で、1 つあたりの上限は無い
月間の受信数ワークスペース単位で、全アプリの合計。下の表

受信の月間上限(ワークスペース単位・UTC の暦月)

StarterPlusTeamBusiness
受信数 / 月500 通3,000 通10,000 通30,000 通

重複を除いた後に 1 通 1 回数えます。80%・100%・150% に達すると、ワークスペースの Owner と Admin に通知します。

  • 100% を超えたら:受信と保存は続けますが、超えた分のメールでは on_receive を起動しません。コンソールでそのメールに印が付きます。プランを上げた後に、コンソールから on_receive を起動できます。
  • 150% に達したら:ワークスペースのアドレスがメールを拒否し、送信元にエラーが返ります。翌月 1 日 0 時(UTC)か、プランを変更すると受信を再開します。切り替わるまでの間に届いた数通は保存されることがあります。

表を消した、主キーを外した、トリガーで行を弾いた、inbound/ に別のファイルが既にある、などで表に書けなかったメールは、Keelson が保留します。コンソールのメールの履歴に、理由と直し方が出ます。表を直した後、コンソールから保存し直してください。

  • 保留は 7 日、アプリあたり 1,000 通までです。保留が 1,000 通に達している間は、そのアプリのアドレスへの新しいメールを拒否します。
  • 7 日を過ぎた保留のメールは「期限切れ」と記録し、メールと添付ファイルを消します。

コンソールのメールの履歴には、保存待ちのメール、メールとして読み取れなかったもの、受信をやめたために破棄したメールも出ます。

spam_score は簡易的な採点です。Keelson はこれで隔離や削除をせず、すべてのメールを保存します。転送したメールは SPF に落ちやすく、普通のメールにも点が付くことがあります。点が高いメールをどう扱うかは、アプリで決めてください。

  1. コンソールを見る:アプリの Email 設定のメールの履歴を開きます。そこにあれば Keelson は受け取っています。状態(保留とその理由)を確かめます。
  2. アドレスを確かめる:作り直す前のアドレスや、受信をやめたアプリのアドレスへのメールは拒否されます。
  3. 上限を確かめる:月間上限の 150% に達している、または保留が 1,000 通に達していると、新しいメールは拒否されます。25 MiB を超えるメールも拒否されます。
  4. 転送の設定を確かめる:Gmail の転送は、確認コードを入力するまで始まりません。Microsoft 365 は、管理者が許可しないと外部への転送を止めます。
  5. Keelson より手前の認証検査:SPF と DKIM の両方に落ちたメールや、送信元ドメインの DMARC ポリシーに落ちたメールは、Keelson に届く前に受信サービスが拒否します。このメールは Keelson に記録が残らず、送信元には通常エラーが返ります。メールの中身を変える転送でこうなることがあります。Google グループ経由の転送なら、こうした送信元のメールも届きます。

keelson dev email は、保存したメールのファイル(.eml)を、Keelson が受け取ったときと同じ形で手元のデータベースと Files に保存します。

Terminal window
export KEELSON_DB_URL=file:local.db
keelson dev email ./samples/order.eml
keelson dev email <file.eml> [--to <address>]
  • メールを Keelson と同じ規則で読み取り、KEELSON_DB_URL のデータベースの keelson_inbound_emails に 1 行 INSERT します。表が無ければ作ります(手元だけ)。
  • 添付ファイルは KEELSON_FILES_DIR(既定 ./.keelson/files)の inbound/<email_id>/<attachment_id> に書きます。手元の開発では Files SDK がここを読みます。
  • KEELSON_DB_URL は手元を指す必要があります。file: の URL か、ホストが 127.0.0.1 か localhost の http://・libsql://・ws:// の URL です。それ以外や未設定のときは、何も書かずに終了コード 2 で終わります。
  • --to は受信アドレス(envelope_to)です。既定は [email protected] です。
  • 出力には email_id・件名・添付ファイルの数と置き場が出ます。
  • email_id はメールの中身と --to から決まります。同じファイルをもう一度流すと Already stored と出て、終了コード 0 で終わります。
  • メールとして読み取れないファイルは終了コード 1 です。
  • on_receive は起動しません。keelson.yaml に宣言があれば、タスクを動かすコマンドを表示します。
Terminal window
keelson dev task run process-mail --payload '{"email_id":"…"}'

.eml ファイルは、メールソフトで元のメッセージをダウンロードして作ります(Gmail では メッセージのソースを表示 → 元のメッセージをダウンロード)。

フレームワーク別の注意点

このページでは、フレームワークごとの本番向けの起動方法と注意点を説明します。Web サーバーは、環境変数 PORT で指定されたポートで起動してください。データベースや通信方式に制約があるフレームワークもあるため、対応アプリと制約も確認してください。

開発サーバーは、性能やエラー画面での内部情報の表示、終了時のリクエスト処理などに問題があるため、本番向けのサーバーを使用してください。

通常の設定は AI エージェントに任せられます。このページは、起動方法を確認したいときや、デプロイ時の問題を調べるときに、自分のフレームワークの箇所を参照してください。

サーバー側のアプリに共通する設定

Section titled “サーバー側のアプリに共通する設定”
  • 0.0.0.0 と環境変数 PORT で指定されたポートで待ち受ける。 127.0.0.1 ではリクエストが届きません
  • 起動コマンドにインストール処理を含めない。 依存パッケージは requirements.txt / pyproject.toml / package.json / go.mod などをもとに、ビルド時にインストールされます
  • 停止は SIGTERM → 10 秒後に SIGKILL。 処理中のリクエストは 10 秒以内に終わらせます
  • worker 数はまず 1 にします。 増やす場合は、利用できる CPU・メモリとアプリの処理内容に合わせて調整します
  • デバッグモードは既定で無効にします。 デバッグ画面は環境変数(DB の認証トークンを含む)を表示することがあります
  • 「Keelson 上か」の判別は KEELSON_MODE。 値が keelson ならプラットフォーム上です。実行環境の判定と、デバッグモードの有効・無効は分けて扱います
import os
ON_KEELSON = os.environ.get("KEELSON_MODE") == "keelson"
DEBUG = os.environ.get("DEBUG", "false").lower() == "true" # 既定は false
command: "uvicorn main:app --host 0.0.0.0 --port $PORT"
env:
PYTHONUNBUFFERED: "1"

--reload は付けません(ファイル監視がメモリを消費し、二重起動することがあります)。worker は既定の 1 のままにします。

app.run() は開発サーバーです。gunicorn を requirements.txt に追加して起動します。

command: "gunicorn --bind 0.0.0.0:$PORT --workers 1 --threads 8 --timeout 0 --graceful-timeout 9 app:app"
env:
PYTHONUNBUFFERED: "1"

app:app はモジュール名とアプリオブジェクト名に置き換えます。--graceful-timeout 9 で 10 秒の SIGTERM 猶予に収めます。

Django は、外部 DB(PostgreSQL など)に接続する構成でのみデプロイできます。Managed SQLite(libSQL)には Django ORM 用のバックエンドがないため、Managed SQLite やファイル SQLite を DB にする構成は未対応です。

db.mode: none にし、接続情報はシークレットで渡します。起動は runserver ではなく gunicorn を使います。

command: "gunicorn --bind 0.0.0.0:$PORT --workers 1 --threads 8 --timeout 0 --graceful-timeout 9 config.wsgi:application"
db:
mode: none
secrets:
items:
- name: DATABASE_URL

config.wsgi:application はプロジェクトの WSGI モジュールに置き換えます。DEBUG は既定で無効にし、静的ファイルは whitenoise で配信します(STATIC_ROOT を設定し、ビルドで collectstatic を実行)。ALLOWED_HOSTS = ["*"] は維持してください。詳しい設定は Deploy Spec を参照してください。

ファイル SQLite を使う構成では、sqlalchemy-libsql-native を使って Managed SQLite に接続する方法があります。この方法は実験的な扱いです。接続設定の変更に加えて、モデル・クエリ・マイグレーションが動作するかを確認してください。AI エージェントに、必要な変更と互換性の確認を依頼してください。

Streamlit は WebSocket が必須のため、現在は動きません。Gradio は 4 以上を検証中です。

NODE_ENV は自動設定されません。keelson.yaml で宣言します。未設定だと Express などがスタックトレースをレスポンスに含めます。

env:
NODE_ENV: "production"

Keelson 上でのビルドには npm を使います。pnpm-lock.yaml / yarn.lock しかない場合は、事前チェックでデプロイが止まります。package-lock.json を生成し、npm で依存のインストールとビルドが成功するかを確認してください。静的サイトは、手元で pnpm や Yarn を使ってビルドしたものも配信できます。詳しくは依存関係のインストールを参照してください。

command: "npm start" # package.json: "start": "node server.js"
  • エラーハンドラでスタックを返さない
  • クライアント IP や HTTPS の判定に trust proxy を使う場合は、実際のプロキシ経路と転送ヘッダーに合わせて設定する。固定値 1 で正しく判定できるとは限りません(Express の公式ガイド)
  • アプリ独自のセッションや JWT に使うシークレットは secrets で宣言する

ビルドして next start で起動します。next dev は本番用ではありません。

command: "npm run start" # package.json: "start": "next start -p $PORT"
  • npm run build がビルド時に自動実行されます。.next/ が生成されることを確認します
  • "start": "next dev" になっていたら直します
  • KEELSON_DB_URL をサーバー側で読みます。NEXT_PUBLIC_* に入れるとブラウザに配布されます

Managed SQLite に接続する場合も、Prisma の provider は "sqlite" を使います。変更するのは接続方法です。ローカルファイルへの接続を、@prisma/adapter-libsql を渡した Prisma Client に変更し、KEELSON_DB_URL と KEELSON_DB_AUTH_TOKEN を使います(接続方法)。

@prisma/adapter-libsql と @prisma/client は対応するバージョンにそろえます(6.x で検証済み)。アダプターの設定だけで、Prisma のマイグレーションがリモート DB に適用されるわけではありません。利用中のバージョンでのクエリの動作と、生成した SQL を Managed SQLite に適用する方法を、AI エージェントに確認してもらってください。

Keelson が go build -o /workspace/app . でビルドし、./app で起動します。

runtime: go-slim
command: "./app"
  • CGO_ENABLED=0 でビルドされます。mattn/go-sqlite3 などの cgo SQLite ドライバは使えません。Managed SQLite には純 Go の libSQL クライアントを使います
  • ListenAndServe を放置せず、signal.NotifyContext と server.Shutdown で 10 秒以内に処理中のリクエストを処理し切ります
  • GORM などの ORM を使っている場合は、接続先 DB とドライバの互換性を確認します。Managed SQLite に接続できない場合は、database/sql と純 Go の libSQL クライアントを使う実装に変更します

Vite、Next.js の静的エクスポート、Astro などは、手元でビルドした出力を assets.dir に指定してデプロイします。command のない静的サイトでは、Keelson 上で依存パッケージのインストールやビルドは行われません。

静的サイトには type: web が必要です。dist/ を .gitignore に入れていても、assets.dir に指定していれば送信対象に含まれます。設定例は keelson.yaml の設定を参照してください。

静的な画面と API サーバーを組み合わせるハイブリッド構成でも、画面のファイルは手元でビルドしてからデプロイします。

デプロイと運用


デプロイする

作ったアプリを Keelson で使えるようにする操作を「デプロイ」と呼びます。初めてアプリを使うときや、変更した内容を反映するときに行います。

Keelson は、AI エージェント経由での操作を前提に設計されています。初期設定を済ませれば、AI エージェントに依頼するだけで、必要な設定の確認からデプロイ、動作確認まで進められます。

新規作成には Owner / Admin / Developer のロール、既存アプリの更新には、そのアプリの管理権限(manage)が必要です。

アプリのフォルダを AI エージェントで開き、次のように依頼します。Keelson のスキルの設定方法はクイックスタートにあります。

このアプリを Keelson にデプロイしてください。必要な設定を確認し、完了したらアプリの URL と動作確認の結果を教えてください。

エージェントには、次の内容を確認・実行してもらいます。

  1. アプリが扱うデータの保存先や起動設定が Keelson に合っているか確認する
  2. keelson.yaml を作成・更新し、必要な API キーなどの登録先を案内する
  3. デプロイを実行し、完了まで確認する
  4. 失敗した場合はログを調べ、原因と対応方法を説明する
  5. 完了後に動作を確認し、確認した内容を報告する

初回デプロイでアプリが作られます。2 回目以降は、同じワークスペース内の keelson.yaml の slug に対応するアプリが更新されます。slug はアプリを識別する名前です。

Web アプリや静的サイトは、報告された URL を開き、画面表示や主な操作を確認します。確認する項目やエージェントへの依頼例はデプロイ後の確認を参照してください。

定期実行だけのアプリは、画面を開く代わりに、コンソールの「定期実行」タブでスケジュールと実行結果を確認します。手動で試す方法は定期実行ジョブにあります。

変更反映のタイミング
コード、keelson.yaml の設定デプロイで反映
シークレット保存後、次のデプロイで反映。「適用」でも、コードを再アップロード・ビルドせずに再デプロイして反映できる
アプリ権限、IP 制御、公開 URL の変更デプロイなしで反映。通常は 2 分以内。障害中の変更は復旧後に反映

公開 URL をアプリ内部でも使っている場合は、URL 変更後の確認も参照してください。

エラーメッセージに含まれるエラーコード(code)や対処のヒント(hint)を AI エージェントに伝えると、原因の調査に役立ちます。ログの確認方法と症状別の対処はトラブルシューティングにあります。

更新の切り替え前に失敗した場合、以前のバージョンへのアクセスは維持されます。ただし、データベースに加えた変更は自動では戻りません。データベースの変更を含む場合は、更新と復元の手順で状態を確認します。

エージェントを介さず、ターミナルから実行することもできます。

Terminal window
# 初回(アプリを作ってデプロイ)
keelson deploy --new
# 初回にシークレットも登録する場合
keelson deploy --new --secrets-from-env-file .env.keelson
# 2 回目以降
keelson deploy
# アップロードせずに設定とアーカイブ内容を確認する
keelson deploy --check --json
  • --secrets-from-env-file で指定したファイルはアーカイブから除外されます
  • 複数のワークスペースに属している場合は --workspace <slug> を付けます
  • 更新先のアプリを明示するには --app <slug> を付けます。アップロードするソースは、コマンドを実行したフォルダのものです
  • スクリプトから使う場合は keelson deploy --ndjson --yes を使うと、完了まで待って結果を 1 行 1 JSON で返します

ここからは、設定やログを詳しく確認するときの説明です。サーバーで画面や API の処理を行う Web アプリでは、次の処理が行われます。

  1. 検証 — keelson.yaml とアップロードするファイルを確認します
  2. ビルド — 必要なライブラリをインストールし、実行に必要なファイルをまとめます。このまとまりを「コンテナイメージ」と呼びます(ビルドの上限は 600 秒)
  3. 起動 — 新しいコードと設定でアプリを起動します。この実行単位を「リビジョン」と呼びます
  4. ヘルスチェック — アプリが応答するか、health.path(既定 /)と verify のパスで確認します
  5. マイグレーション — DB の構造などを更新する db.migrate があれば実行します
  6. 切り替え — 利用者のアクセス先を新しいリビジョンに移します

静的サイト / SPA は、ビルド済みの HTML・JavaScript などを配信します。Web サーバーの起動は行いません。定期実行だけのアプリには Web のアクセス先を切り替える処理はなく、設定した時刻にジョブが実行されます。

データの保存先や起動設定を調整する例として、ローカルの SQLite ファイルから Managed SQLite への移行や、Keelson が渡す PORT を使う変更があります。詳しくは対応アプリと制約を参照してください。

デプロイ後の確認

デプロイが完了したら、アプリを開き、普段使う操作ができるか確認します。AI エージェントには、画面や API が正常に応答するかの確認を依頼できます。

  • 自分のアカウントでアプリを開けるか
  • 一覧表示や検索など、主な操作ができるか
  • 保存機能がある場合、テスト用のデータを保存して読み直せるか

利用者に共有する前には、その人がアプリを開ける権限を持っているかも確認します。自分のアカウントで開けても、他の利用者が開けるとは限りません(アプリ URL とドメイン)。

定期実行だけのアプリは、コンソールの「定期実行」タブで実行結果を確認します。

デプロイしたアプリの主な画面と API を確認してください。確認できたことと、まだ確認していないことを教えてください。データを変更する確認にはテスト用データを使ってください。

画面や API から応答が返っても、計算結果や業務上のルールが正しいかは別途確認が必要です。普段使う入力例と期待する結果を伝えると、エージェントも確認しやすくなります。

エージェントが応答を確認する方法

Section titled “エージェントが応答を確認する方法”

通常のアプリの URL は認証で保護されているため、ログイン情報を付けない curl では確認できません。keelson app curl は、CLI にログインした本人の権限でリクエストを送ります。

本人としてアプリにアクセスするための一時的な認証情報を「プレビュートークン」と呼びます。app curl は内部でトークンを取得するため、利用者が値を扱う必要はありません。

1 回の応答を確認する(keelson app curl)

Section titled “1 回の応答を確認する(keelson app curl)”

次のコマンドで、ページや API の応答を取得できます。

Terminal window
keelson app curl / # GET /
keelson app curl -i /api/items # ヘッダーも表示

データの登録・更新・削除やファイルのアップロードは、実際のアプリに変更を加えます。次は書き込みの例です。パスとデータは、確認するアプリに合わせます。

Terminal window
keelson app curl /api/items --method POST \
--data '{"name":"example"}' \
--header 'Content-Type: application/json'
keelson app curl /upload --form title=example --form [email protected]
  • レスポンス本文は標準出力、メソッド・URL・ステータスは標準エラー出力に出ます。-i を付けるとヘッダーも標準エラー出力に出るので、本文だけをファイルに落とせます
  • 既定は GET。--method で POST / PUT / PATCH / DELETE を明示すると、有効期間 5 分の書き込みを許可するトークンが使われます
  • --data は書き込みメソッドとだけ併用できます。@ファイル名 でファイルから読みます
  • --form を使うとメソッドは POST になり、multipart の Content-Type は CLI が付けます(--header での上書きは不可)
  • パスは / で始める必要があります。絶対 URL と // で始まるパスは拒否されます。リダイレクトは追いません
  • /__keelson/media/... と /__keelson/assets/... も GET / HEAD で確認できます
  • 対象のアプリを明示する場合は --app <slug>

エージェントには、成功を示すステータスだけでなく、返された内容の種類(Content-Type)も確認してもらいます。たとえば、存在しない JavaScript ファイルの代わりに SPA の HTML が返ると、200 でも画面は正常に動きません。

別のツールや複数のリクエストで確認する(keelson preview)

Section titled “別のツールや複数のリクエストで確認する(keelson preview)”

別の HTTP クライアントを使う、複数回リクエストする、有効期間を選ぶ、といった場合はトークンを直接発行します。

Terminal window
keelson preview # GET / HEAD のみ。既定 30 分
keelson preview --ttl 5m
keelson preview --allow-writes # POST / PUT / PATCH / DELETE も可。既定 5 分、最大 10 分
keelson preview --json # トークン・URL・有効期限を JSON で

--json が返す token と app_url を別の HTTP クライアントで使います。トークンは Bearer トークンとして渡します。

Terminal window
curl --header 'Authorization: Bearer <token>' '<app_url>/api/items'

トークンは標準出力に 1 回だけ表示され、Keelson は保存しません。ログや報告書に残さないでください。keelson preview で発行できるトークンは、アプリ × 利用者につき 1 本です。もう一度発行すると、有効期限内でも前のトークンは取り消されます。keelson app curl は別の内部用のトークンを使うため、preview で発行したトークンを取り消しません。

現在配信中のデプロイを自分が作っていない場合、プレビュートークンの発行前に、ブラウザで開く承認 URL が案内されます。承認には、そのアプリの管理権限(manage)が必要で、管理権限がある本人が承認することもできます。

承認画面で対象アプリ、書き込みの許可、有効期間を確認し、トークンの発行を承認します。承認後、案内された --confirmation <id> を付けて同じコマンドを再実行します。

この確認で分かること・分からないこと

Section titled “この確認で分かること・分からないこと”
  • これらのコマンドで取得できるのは、本人の権限で送ったリクエストへの応答です。ブラウザでの見た目や操作、他の利用者のアクセス権限は別途確認します
  • 静的サイトには書き込み先がないので、書き込みメソッドは拒否されます
  • 読み取り用の GET でも、アプリ側の実装によってはデータの変更やメール送信が起こります。確認対象の処理をエージェントに調べてもらい、意図しない変更が起きるパスは避けます

環境変数とシークレット

外部サービスの API キーや接続先などを、アプリに設定できます。設定値をアプリに渡す仕組みを「環境変数」と呼びます。API キーなどの秘密の値は、Keelson の「シークレット」に登録します。

種類例どこに書くか
秘密でない設定値NODE_ENV、LOG_LEVEL、公開 API の URLkeelson.yaml の env
秘密の値API キー、トークン、DB 接続文字列、Webhook の署名シークレットシークレット(コンソール / CLI / デプロイ時の env ファイル)

keelson.yaml はソースコードと一緒に管理するファイルです。秘密の値は含めず、シークレットに登録します。

必要な設定を AI エージェントに確認する

Section titled “必要な設定を AI エージェントに確認する”

このアプリに必要な設定値と API キーを整理してください。設定ファイルには名前と用途を記載し、私が値を入力するファイルを用意してください。入力後に Keelson への登録と反映をお願いします。

設定ファイルの作成、アプリ側で値を読み取る処理、Keelson へのシークレットの登録・反映まで、エージェントに依頼できます。秘密の値はチャットに貼る代わりに、手元の設定ファイルへ入力して、そのファイルから登録してもらいます。コンソールから自分で入力することもできます。

Keelson のスキルを設定した AI エージェントに、手元の env ファイルからシークレットを登録するよう依頼できます。初回デプロイ時の登録も、既存アプリの値の更新も依頼できます。

.env.keelson に入力した値を、このアプリのシークレットに登録して反映してください。値そのものはチャットやログに出力しないでください。

以下のファイルは書式の例です。必要なキーをエージェントに用意してもらい、値を入力します。

.env.keelson
OPENAI_API_KEY=sk-...
SLACK_WEBHOOK_URL=https://hooks.slack.com/...

自分で登録する場合は、既存アプリの「シークレット」タブで追加・更新・削除できます。保存後に表示されるのは名前と更新日時で、値は再表示されません。

CLI から登録する場合は、次のコマンドを使います。エージェントもこれらのコマンドで登録・反映できます。

Terminal window
# 初回デプロイと同時に登録
keelson deploy --new --secrets-from-env-file .env.keelson
# 既存アプリの更新と同時に登録
keelson deploy --secrets-from-env-file .env.keelson
# 値を登録し、アップロード・ビルドなしの再デプロイで反映
keelson secrets set --from-env-file .env.keelson --apply

--secrets-from-env-file で指定したファイルはアップロード対象から除外されます。秘密を含むファイルは Git に追加せず、値をコマンドに直接書くことも避けてください。コマンドに書いた値はシェル履歴に残ることがあります。

登録した値をアプリに反映する

Section titled “登録した値をアプリに反映する”

シークレットは保存しただけでは、既に動いているアプリに反映されません。エージェントには登録と反映をまとめて依頼できます。初回はデプロイ時に反映され、既存アプリでは次のいずれかで反映します。

  • コードも更新する場合は、keelson deploy でデプロイする
  • 値だけを反映する場合は、コンソールに表示される「未反映の変更があります」の 適用ボタンを押す
  • CLI で登録と反映をまとめて行う場合は、上の secrets set --apply を使う

「適用」と --apply は、コードを再アップロード・ビルドせずに再デプロイします。最後に成功したデプロイの実行用ファイル(コンテナイメージ)を使い、新しい値でアプリを起動します。完了後は、API キーを使う操作などが動くか確認します。

ここからは設定ファイルとコードの詳細です。秘密でない値は、エージェントに keelson.yaml の env への記載を依頼できます。

env:
NODE_ENV: "production"
LOG_LEVEL: "info"
  • 値は必ず引用符で囲みます(数値・真偽値も)。引用符がないとデプロイが拒否されます
  • KEELSON_ で始まる名前は書けません(Keelson が設定します)。PORT は書いても無視され、Keelson の値が使われます
  • 変更は次のデプロイで反映されます

必要なシークレットを設定ファイルに記載する

Section titled “必要なシークレットを設定ファイルに記載する”

keelson.yaml の secrets には、アプリが使うシークレットの名前・用途と、登録が必要な条件を記載できます。値は書きません。この宣言は任意です。 宣言しなくても、登録したシークレットは環境変数としてアプリに渡されます。

宣言すると、必要な設定が分かりやすくなり、登録漏れをデプロイ時に検出できます。エージェントに、アプリが使うキーと必須条件の整理を依頼できます。

secrets:
items:
- name: OPENAI_API_KEY
description: "OpenAI API key"
- name: SLACK_WEBHOOK_URL
description: "通知先の Slack Webhook"
required:
- all_of: [OPENAI_API_KEY]
message: "OPENAI_API_KEY を設定してください"
  • items は名前と用途の一覧で、コンソールにも表示されます。ここに書くだけでは必須にはなりません
  • required はデプロイに必要な条件です。条件を満たすシークレットが未登録だと、理由が表示され、デプロイは失敗します。この例では OPENAI_API_KEY が未登録だと「OPENAI_API_KEY を設定してください」と表示されます。値を登録してから、もう一度デプロイします
  • SLACK_WEBHOOK_URL は一覧にありますが、必須条件には含まれていないため、未登録でもこのチェックではデプロイを止めません

required を書かない場合、Keelson はこの必須チェックを行いません。必要な値が不足していてもデプロイ時のチェックでは検出されず、アプリの起動時や API キーを使う操作時にエラーになることがあります。

範囲(スコープ)設定場所用途
ワークスペースコンソールの「シークレット」全アプリ共通の値(共有 API キーなど)
アプリアプリ画面の「シークレット」タブそのアプリだけの値、またはワークスペースの値の上書き

同じ名前があれば アプリ > ワークスペース > env の順で優先されます。アプリ画面のシークレットタブには、どちらのスコープから来た値かが表示されます。

env もシークレットも、サーバー側のコードから環境変数として読み取ります。

import os
api_key = os.environ["OPENAI_API_KEY"]
const apiKey = process.env.OPENAI_API_KEY;

env もシークレットも、ビルド(依存関係のインストール、npm run build)には渡りません。ビルドが読めるのはアップロードしたソース一式だけです。

フロントエンドのビルドに埋め込む公開値(VITE_API_URL など)は、ソース内の設定ファイルに書きます。ブラウザに配布される値なので、ここに認証情報を置かないでください。

システムが自動設定する環境変数

Section titled “システムが自動設定する環境変数”

Keelson はプラットフォーム管理の変数を各アプリに設定します。自分で定義または上書きしないでください。

変数内容
PORTWeb アプリの待ち受けポート。
TZワークスペースのタイムゾーン。
KEELSON_MODEプラットフォームモードを示す値。
KEELSON_APP_IDアプリの内部 ID。
KEELSON_WORKSPACE_IDワークスペースの内部 ID。
KEELSON_TENANT_IDKEELSON_WORKSPACE_ID の互換用別名(値は同じ)。
KEELSON_DEPLOY_ID現在のデプロイの内部 ID。
KEELSON_APP_URLアプリの標準 URL。
KEELSON_DIRECTORY_BASE_URLDirectory API の呼び出し先 URL。

機能を有効にすると追加の変数が設定されます。完全な一覧、各変数の設定条件、互換性のために残されている変数は環境変数一覧にあります。

NODE_ENV は自動設定されないため、アプリが必要とする場合は env に記載します。

アプリ URL とドメイン

Web アプリや静的サイトには専用の URL が割り当てられ、デプロイ後に利用者へ共有できます。自社で管理するドメインを使うこともできます。

標準の URL は次の形式です。slug は、URL などに使う識別名です。

https://<ワークスペース slug>--<アプリ slug>.keelson.run

ワークスペースの slug が acme、keelson.yaml の slug が dashboard なら https://acme--dashboard.keelson.run です。HTTPS の証明書は Keelson が管理します。

URL は、コンソールのアプリ画面、デプロイ完了時の AI エージェントの報告、keelson status で確認できます。

URL を知っているだけでは開けません。ワークスペースにログインし、そのアプリの閲覧権限を持つメンバーだけが開けます。IP 制御を設定していれば、許可されたネットワークからのアクセスに限られます。

App User にはアプリの URL を別途共有する必要があります。App User はコンソールを開けないため、自分で URL を確認する手段がありません。上のいずれかの方法で URL をコピーし、チャットやメールで本人に送ってください。

アプリ作成後も URL は変えられます(Owner / Admin)。コンソールのアプリ設定「公開 URL」から行います。

変更すると以前の URL からは開けなくなり、新しい URL への自動転送もありません。利用者に新しい URL を共有し、ブックマークや外部サービスの設定を更新します。影響を確認するチェックを付けてから変更します。

  • 以前の URL は 30 日間予約され、他のアプリが同じ名前を使えません。その間のアクセスには、利用できなくなったことを示す 410 Gone が返ります。30 日後に自動で解放されます
  • URL の変更にはコンソール、または keelson apps rename <new_slug> を使います。keelson.yaml の slug はデプロイ先のアプリを識別する名前で、これだけを変えても URL は変わりません。--app で対象を指定しない場合、次のデプロイは変更後の slug に対応するアプリを探すため、元のアプリを更新できなくなります。コンソールでアプリの識別名も変更した場合は、次のデプロイ前に keelson.yaml の slug を合わせます

ワークスペースの slug も設定から変更できますが、既存アプリの URL は変わりません。新しいワークスペース slug の形式に揃えたい場合は、アプリごとに公開 URL を更新します。

Team プランでは、app.example.com のような自社のサブドメインでアプリを利用できます。設定には、ドメインの管理画面で DNS を変更できる権限が必要です。社内のドメイン管理者に依頼する場合は、Keelson が表示する設定値を共有します。

  • 対象: Team プラン。ワークスペース合計で 10 件まで、1 アプリに 1 ドメイン
  • サブドメインのみ。ルートドメイン(example.com)とメールドメインは対象外。自社のドメインからメールを送るには自社のドメインから送るを参照
  • カスタムドメイン経由でも、認証・権限・IP 制御は同じように適用されます

設定は次の順で行います。

  1. Owner または Admin が、コンソールのアプリ設定か keelson domains add <hostname> [--app <slug>] でドメインを登録します。登録すると、DNS に設定する CNAME の転送先が表示されます
  2. DNS 事業者の設定で、登録したサブドメインの CNAME を表示された転送先へ向けます。本番環境の転送先は custom-origin.keelson.run です。コンソールまたは CLI に表示された値を使用してください
  3. コンソールの状態表示か keelson domains verify <hostname> [--app <slug>] で有効になったことを確認します。有効化には数分かかることがあります

所有権の確認、HTTPS 証明書の発行、配信の設定は Keelson が自動で行います。

アプリの中から自分の URL を知る

Section titled “アプリの中から自分の URL を知る”

メール内のリンクなど、アプリが自分の URL を組み立てる場合の実装方法です。設定は AI エージェントに依頼できます。

アプリの標準 URL は環境変数 KEELSON_APP_URL で渡されます。リクエストの Host ヘッダーは内部のホスト名になるので、絶対 URL が必要な場面(メール内のリンク、リダイレクト、og:url)では KEELSON_APP_URL を使います。

import os
base_url = os.environ["KEELSON_APP_URL"] # 例: https://acme--dashboard.keelson.run

ローカル開発では設定されません。「Keelson 上で動いているか」の判別には KEELSON_MODE を使ってください。

公開 URL を変更しても、動いているアプリの環境変数はその場では更新されません。KEELSON_APP_URL を使うアプリは再デプロイし、メール内のリンクや移動先が新しい URL になっているか確認します。

カスタムドメインを設定しても、KEELSON_APP_URL は標準 URL のままです。メールのリンクなどにもカスタムドメインを使いたい場合は、その URL をアプリ側の設定値として持つようエージェントに依頼します(環境変数とシークレット)。

アプリの状態とログ

アプリが使える状態か確認したり、不具合の原因を調べたりするときは、コンソールの状態表示とログを確認します。ログには、アプリの処理やエラー、アクセスの記録が残ります。

コンソールのアプリ一覧とアプリ画面に、状態がバッジで表示されます。

状態意味
稼働中最近のアクセスにより、「同時に使えるアプリ数」の枠を 1 つ使っている
スリープ中デプロイ済みで、最近のアクセスによる枠を使っていない。停止しているアプリはアクセス時に自動で起動する
配信中静的サイト / SPA を配信している。画面のファイルは Keelson が直接返す
サスペンド手動で停止した状態。アクセスがあっても起動・再公開しない。データ・URL・設定は保持される
起動中 / デプロイ中起動・停止の切り替え、または新しいコードや設定の反映を処理している
未デプロイアプリは作られているが、まだデプロイされていない
エラー直近のデプロイまたは実行に失敗した。アプリ画面に原因が表示される
削除中削除処理中。完了すると一覧から消える
—定期実行だけのアプリ。ジョブごとの実行結果は「定期実行」タブで確認する

「稼働中」「スリープ中」は利用状況の表示で、サーバーのプロセスが起動しているかを直接示すものではありません。Web アプリは、アクセスがない状態が続くと停止し、次のアクセスで起動します(アプリが動くタイミング)。

通常は、直近 5 分にアクセスがあったアプリが枠を使います。優先起動で予約した枠は、アクセスがなくても確保されます。枠が空いていないときは、空くまで待つよう案内する画面が表示されます。予約していない枠は、そのアプリへのアクセスが約 5 分途絶えると空きます。

「エラー」は直近のデプロイの失敗を示す場合もあり、以前のバージョンが使えることがあります。「概要」の失敗内容と、アプリを実際に開けるかを合わせて確認します。

一時的に止めたいアプリは、アプリ画面の「設定」→「サスペンド」でサスペンドします。対象は Web アプリと静的サイト / SPA です。サスペンド中は、アクセス・Webhook・cron のいずれでも起動せず、静的ファイルも配信されません。Keelson の標準 URL には停止ページが表示され、独自ドメインの配信も止まります。データ・URL・設定は削除されません。

再開はアプリ画面のヘッダーの「再開」から行います。静的サイトや SPA はサスペンド中にデプロイしても停止状態を維持し、明示的に再開したときに配信へ戻ります。静的ファイルの停止と再公開は順次反映されるため、操作後に配信状態が切り替わるまで少し時間がかかることがあります。

定期実行だけのアプリには、コンソールのサスペンド操作はありません。各ジョブの enabled を false にしてデプロイすると、定期実行を止められます。再開するときは true に戻してデプロイします(定期実行ジョブ)。

アプリ画面のヘッダーの「優先起動」を ON にすると、「同時に使えるアプリ数」の枠を常に 1 つ予約します。枠が埋まっていて起動できない、という状態を防げます。

  • 枠を予約していても、アプリはアクセスがない状態が続くと停止します。次のアクセス時には起動を待つことがあります
  • 有料契約中は Plus 以上で、枠数より 1 つ少ない数まで設定できます。トライアル中は全プランで 2 本まで設定できます

調べたいことに合わせて、次の場所を確認します。

調べたいこと確認する場所記録される内容
デプロイが失敗した理由「デプロイ」の各デプロイにある「詳細」失敗の要約と、keelson.yaml についての警告などの利用者向けメッセージ。「詳細」は失敗したデプロイと警告のあるデプロイにだけ出ます
アプリ使用中のエラー「ログ」のアプリログアプリが出力した処理の記録やエラー(標準出力・標準エラー)
利用者がアクセスを拒否された理由「ログ」のアクセスログ誰が・いつ・どのパスにアクセスし、許可・拒否されたか
定期実行が成功したか「定期実行」の実行履歴実行ごとの開始時刻・結果・所要時間・ログ

AI エージェントには、アプリ名・発生時刻・起きたことを伝えて調査を依頼できます。

このアプリで[日時]に[操作]をするとエラーになりました。状態とログを確認し、原因と対応方法を教えてください。

アプリログは Web と定期実行(cron)を切り替えられます。画面には直近 200 行を表示し、エクスポートでは最大 5,000 行を取得できます。アクセスログは、利用者・機械(アプリトークン)・Webhook で絞り込めます。閲覧できる期間はプランによって異なります。

CLI からは keelson logs で取得できます。エージェントが処理しやすい JSON 形式でアプリログを取得するには、keelson logs app <slug> --json を使います。デプロイ失敗時に確認するログはトラブルシューティングにまとめています。

「デプロイ」タブに過去のデプロイが並び、各デプロイのソースをダウンロードできます。失敗したデプロイと警告のあるデプロイには「詳細」も出ます。

コードを前のデプロイに戻す操作を「ロールバック」と呼びます。データベースの内容は戻らず、静的サイト / SPA は以前のファイルを再デプロイする必要があります。手順と戻せる範囲は更新と復元にまとめています。

「再デプロイ」(アプリ画面ヘッダー)は、最後に成功したデプロイの実行用ファイルを使って起動し直す操作です。シークレットの「適用」も、コードを再アップロード・ビルドせずに再デプロイして反映します(環境変数とシークレット)。

アプリを廃止するときは「設定」→「危険な操作」から削除できます。ブラウザでの承認が必要で、利用者自身で取り消すことはできません。削除前に取り出すデータと保持期間は更新と復元を参照してください。一時的に止める場合は、上の「サスペンド」を使います。

更新と復元

アプリの変更を反映したり、不具合が起きたときに以前のコードやデータへ戻したりできます。やりたいことに合わせて操作を選びます。

やりたいこと操作
変更した内容を反映する再度デプロイする
更新前のコードに戻すロールバックする
誤って変更・削除したデータを戻すDB の復元ポイントを使う
一時的に利用を止めるサスペンドする
アプリを廃止する削除する

操作には、そのアプリの管理権限(manage)が必要です。データベースの復元とアプリの削除にはブラウザでの承認も必要です。

コードを戻しても、データベースやファイルは以前の状態に戻りません。 詳しい範囲は、後半の何が戻り、何が戻らないかで確認できます。

AI エージェントには、変更内容と確認してほしい操作を伝えます。

この変更を Keelson に反映してください。デプロイ前に設定とアップロード対象を確認し、完了後に[操作]ができるか確認してください。

  1. 手元の開発環境で変更内容を確認します
  2. エージェントにデプロイを依頼します。CLI では keelson deploy --check --json で設定とアップロード対象を事前確認し、keelson deploy で反映できます
  3. 完了したらアプリを開き、変更した操作を確認します。定期実行だけのアプリは実行履歴を確認します(デプロイ後の確認)

切り替え前に失敗した場合、以前のバージョンへのアクセスは維持されます。ただし db.migrate が途中まで実行された場合、その変更は残ります(下の「DB の構造を変える」)。

変更が反映されるタイミング(シークレットは保存後にデプロイまたは「適用」が必要、など)はデプロイするを参照してください。

コードを以前のデプロイに戻す操作を「ロールバック」と呼びます。対象にできるのは、実行用のファイルをまとめた「コンテナイメージ」が残っている、過去の成功したデプロイです。

エージェントには「[不具合]が起きたので、前のコードに戻せるか、データへの影響も含めて確認してください」と依頼できます。CLI の操作は次のとおりです。

Terminal window
keelson rollback # 直前の成功したデプロイに戻す
keelson rollback <deploy_id> # 指定したデプロイに戻す
  • ビルドし直さず、対象デプロイのイメージと keelson.yaml の設定でアプリを起動します
  • 対象にできるのは、イメージを持つ完了済みのデプロイだけです。静的サイト / SPA のデプロイと、古い方式で作られたデプロイはロールバック先にできません
  • シークレットは現在の値が使われます
  • 対象デプロイの keelson.yaml に db.migrate があれば、ロールバックでも切り替え前に実行されます。DB に一切触れない操作ではありません
  • データベースは戻りません。 更新時に DB の構造(スキーマ)を変えていた場合、戻したコードがその構造で動くか確認が必要です。列の追加でも制約や既定値によって影響が出るため、エージェントに互換性の確認を依頼します。必要に応じて DB の復元も検討します
  • デプロイ進行中は実行できません

デプロイの一覧はコンソールの「デプロイ」タブにあり、各デプロイのソースをダウンロードできます。

静的サイト / SPA を戻すには、以前の成果物をもう一度デプロイします。Git で前のコミットに戻して手元でビルドし直すか、「デプロイ」タブから以前のデプロイのソース(assets.dir の中身と keelson.yaml)をダウンロードします。ダウンロードしたアーカイブを使う場合は、展開して keelson.yaml のあるフォルダで keelson deploy を実行します。再ビルドは不要です。

Managed SQLite(db.mode: libsql)のデータは、コンソールのアプリ画面にある「復元ポイント」から戻します。

  1. 復元ポイントのタイムラインから時点を選びます。日次バックアップ、手動で保存した状態(スナップショット)、指定した時刻への復元(PITR)のいずれかです(プランごとの保持世代数と復元窓はプランと制限)
  2. 選択した過去の復元ポイントをダウンロードして、中身を確認することもできます。この操作で取得するのは、現在の DB ではありません
  3. 復元を実行します。ブラウザでの承認が要ります
  4. 復元後、アプリを開いてデータを確認します

復元で起きること:

  • 復元の切り替え中は、一時的に DB への書き込みができなくなります。 利用者に案内し、利用の少ない時間帯に実行してください
  • その時点以降の書き込みはすべて消えます。 消したデータだけを戻す操作ではありません。現在のデータも残しておきたい場合は、復元を実行する前に現在の DB の手動スナップショットを作成し、そのスナップショットをダウンロードします。復元後に必要な変更を移す作業は、エージェントに相談できます
  • 復元前の状態は 72 時間保持され、その間は復元を取り消せます
  • 復元は 1 つのアプリの DB だけに効きます。他のアプリには影響しません
  • Files / Media のファイルと外部 DB は対象外です

大きな変更の前に手動スナップショットを作ると、戻す時点を残せます(1 アプリ 5 回/日)。

保存する項目の追加など、DB の構造を更新する処理を「マイグレーション」と呼びます。コードを戻したときにも動くよう、エージェントには次の確認を依頼します。

DB の構造を変更する場合は、更新前のコードでも動くか、失敗後に再実行しても問題がないかを確認してください。復元が必要になる変更は、実行前に影響と戻し方を教えてください。

マイグレーションの実装時の注意

Section titled “マイグレーションの実装時の注意”

db.migrate に書いたコマンドは、デプロイやロールバックで新しいバージョンへ切り替える前に実行されます。

  • 失敗すると以前のバージョンへのアクセスは維持されます。ただし、途中まで実行された DB の変更は自動では戻りません。keelson logs deploy <deploy_id> のログと DB の状態を確認してから修正します
  • 同じ処理を繰り返しても重複した変更が起きないようにします。この性質を「冪等性」と呼びます。適用済みの変更を記録する仕組みや IF NOT EXISTS などを使い、途中で失敗した場合も確認します
  • 列やテーブルを追加する場合も、旧コードとの互換性を確認します。列の削除や型の変更など影響の大きい変更は、直前にスナップショットを作成し、利用の少ない時間帯に行う方法を検討します

「止める」と「消す」は別の操作です。

操作何が起きるか戻せるか
サスペンド(Web アプリと静的サイト / SPA の「設定」→「サスペンド」)アクセス・Webhook・cron のいずれでも起動しない。URL には停止ページが出る。データ・URL・設定は残る「再開」でいつでも戻せる
削除(「設定」→「危険な操作」)アプリ本体・URL・デプロイ履歴・シークレットが消える。ブラウザでの承認が要る戻せない

定期実行だけのアプリには、コンソールのサスペンド操作はありません。各ジョブの enabled を false にしてデプロイすると、定期実行を止められます。再開するときは true に戻してデプロイします(定期実行ジョブ)。

削除の前に取り出しておくもの:

  • DB のデータ — 現在のデータが必要なら手動スナップショットを作り、その復元ポイントからダウンロード
  • Files のファイル — アプリにダウンロード処理を用意して取り出します
  • Media のファイル — アプリが記録したファイル ID から閲覧用リンクを作り、削除前に保存します。Files / Media とも、コンソールや CLI には一覧・ダウンロード機能がありません(ファイルとメディア)
  • ソースコード — Git、または「デプロイ」タブの各デプロイからダウンロード
  • シークレットの値 — 保存後は再表示されません。登録時の保管先や発行元で、必要な値を確認します

削除後、DB のバックアップ・Files・非機密の構成メモ(アプリ名、URL、cron 定義、環境変数とシークレットの名前)は 7 日間隔離保持されますが、これは誤削除への裁量対応であり、復元の保証ではありません。

契約が終了した場合のアプリの扱いは料金と契約の運用を参照してください。

表の「ロールバック」はコードを前のデプロイに戻す操作、「DB の復元」は Managed SQLite を復元ポイントに戻す操作です。

対象ロールバックで戻るかDB の復元で戻るか備考
コード(イメージ)戻る戻らないビルドし直さず、対象デプロイのイメージを起動する。静的サイト / SPA は対象外
keelson.yaml の設定(env、crons、assets、health)戻る戻らない対象デプロイ時点の設定が使われる
シークレットの値戻らない戻らない常に現在の値が使われる。コンソールで手で戻す
Managed SQLite のデータ戻らない戻る日次バックアップ、手動スナップショット、PITR(時刻復元)
db.migrate で加えたスキーマ変更戻らない戻る復元ポイントの時点の DB 構造になる
Files SDK のファイル戻らない戻らないバックアップの対象外。アプリ側で保持する
Media SDK のファイル戻らない戻らない上書き不可。差し替えは新しいファイルを保存する
外部 DB(PostgreSQL など)戻らない戻らない外部サービス側のバックアップで戻す
外部サービスへの変更(Slack 投稿、メール送信、決済)戻らない戻らないロールバックでは取り消されない。再実行時の重複に注意
アプリ権限、IP 制御、公開 URL戻らない戻らないデプロイと独立した設定。手で戻す

トラブルシューティング

デプロイに失敗したときや、アプリが期待どおりに動かないときの確認方法をまとめています。AI エージェントには、起きたこととエラーメッセージを伝えて、原因の調査を依頼できます。

症状確認する項目
デプロイできないどこで止まったかを特定する
アプリを開けないアクセス権限、利用枠、応答の時間制限
更新が見えない画面の更新、シークレットの反映
データが残らないデータの保存先
定期実行やバックグラウンド処理が動かない定期実行の確認、処理方法の確認

Keelson のスキルを設定した AI エージェントは、CLI を通じてアプリの状態、デプロイログ、アプリログ、アクセスログを取得できます。CLI にログインしたアカウントの権限で確認するため、対象アプリのログを閲覧できる権限が必要です。ログを自分で探して貼り付けなくても、アプリ名と発生した日時、症状を伝えて調査を依頼できます。

このアプリで[操作]をすると[症状]が起きます。発生したのは[日時]です。状態とログを確認し、原因と対応方法を教えてください。

エラーコード(code)、メッセージ(message)、対処のヒント(hint)が表示されていれば、一緒に伝えます。サービス側の障害など、アプリの修正では解決しない場合は、サポートに問い合わせるの情報をまとめます。

コンソールでは、アプリ画面の「概要」に失敗の要約、「デプロイ」の各デプロイにある「詳細」に失敗の詳細、「ログ」にアプリログがあります。まず概要を確認し、必要に応じてエージェントに詳細を調べてもらいます。

deploy_id はデプロイを識別する ID で、CLI の出力やコンソールの「デプロイ」タブで確認できます。

Terminal window
keelson status # アプリと直近デプロイの状態
keelson diagnose <deploy_id> # 失敗したデプロイの診断
keelson logs deploy <deploy_id> # 進行ログと失敗したデプロイの詳細
keelson logs app <slug> # アプリの標準出力・標準エラー

起動中にアプリが終了した場合も、keelson logs deploy や keelson diagnose から保存済みの起動ログを確認できます。エラーが起きたコードの位置や、必要な設定が足りないことなどが記録されます。

保存されるログの範囲

失敗したデプロイでは、ビルドや起動の詳細が保存されます。依存パッケージや Dockerfile、シークレット、設定、マイグレーションのエラーを後から調べるときにも使えます。起動時の例外の記録(traceback)やプロセスエラーは、保存済みの起動ログを確認します。

成功したデプロイのビルド出力は保存されません。進行ログも保存済みの失敗詳細もない場合は、ログがない理由が表示されます。

止まった場所見るものよくある原因
アップロード前(CLI の事前チェック)コマンドの出力Go アプリに go.mod がない、Node アプリに pnpm / yarn 用ロックファイルしかない、command に pip install、db.mode がない、env の値が引用符なし
検証code と messageworkers / databases が残っている、予約名、プラン上限
ビルドデプロイログ依存の解決失敗、package.json と package-lock.json の不一致、非公開のパッケージ配布先(private registry)、cgo が必要な Go パッケージ、ビルドスクリプトのエラー
起動keelson logs deploy <deploy_id> の保存済みのアプリの起動ログ0.0.0.0 で listen していない、PORT のハードコード、未設定のシークレットで例外
ヘルスチェック保存済みのアプリの起動ログ、その後にアプリログアプリが起動中に終了、/ が 5xx、起動が完了せずヘルスチェックの制限時間を超える
マイグレーションデプロイログdb.migrate の SQL エラー。以前のバージョンへのアクセスは維持されますが、マイグレーションが加えた DB の変更は自動では戻りません。DB の状態を確認してから直します
動いているが期待どおりでないアプリログ、アクセスログ下の「よくある失敗」

Ctrl-C で止まるのは、このクライアントによる監視だけです。デプロイはサーバー側で続いています。 そのまま新しいバージョンへ切り替わることがあるため、CLI に表示された deploy_id で進行状況を確認します。

Terminal window
keelson status <deploy_id>

新しいバージョンを使わない場合は、デプロイの完了後に以前のコードへ戻します。コンテナイメージが残っている過去の成功したデプロイには、次のコマンドで戻せます。DB の変更は戻りません。

Terminal window
keelson rollback --app <slug>

静的サイト / SPA はロールバックの対象外です。以前のファイルを再デプロイします。手順は更新と復元を参照してください。

戻せるデプロイがない場合など、いったん利用を止めたいときは、デプロイ完了後にサスペンドします。

Terminal window
keelson app stop --app <slug>

ローカルでは動くが Keelson で落ちる

Section titled “ローカルでは動くが Keelson で落ちる”

手元と Keelson で設定や保存先が異なる可能性があります。エージェントには、次の点を確認してもらいます。

  • 手元からの接続だけを受け付けている — 待ち受けアドレスを localhost から 0.0.0.0 に変える
  • 接続を受け付けるポート番号をコードで固定している — PORT 環境変数から読む
  • ローカルにしかないファイルに依存している — .env、ローカルの DB ファイル、.gitignore 済みのファイル。keelson deploy --check --json の archive.excluded で、何が除外されたか分かります
  • 環境変数が未設定 — ローカルの .env にだけある値は Keelson にはありません。secrets に宣言して値を設定する
  • ローカルの SQLite ファイルを使っている — /tmp やアプリディレクトリのファイルは再起動後に残りません(/data は存在せず、作成もできません)。Managed SQLite への移行を検討する

Node アプリのロックファイルが拒否される

Section titled “Node アプリのロックファイルが拒否される”

Keelson の Node ビルダーは npm を使うため、依存を固定するには npm 用の package-lock.json が必要です。エージェントに、プロジェクトのルートで npm install を実行して package-lock.json を生成し、依存関係の整合性を確認してから再デプロイするよう依頼します。生成したファイルはソースコードと一緒に管理します。

pnpm-lock.yaml や yarn.lock は削除しなくてかまいません。package-lock.json と同居していてもデプロイでき、ビルドには package-lock.json が使われます。

保存先がアプリのサーバー内のファイルになっていないか確認します。/tmp やアプリディレクトリのファイルは、再起動・再デプロイ・アイドル停止で失われます。データベースか Files / Media SDK に置きます。

バックグラウンド処理が動かない

Section titled “バックグラウンド処理が動かない”

サーバー側でレスポンスを返した後に続ける処理は、最後まで実行されることが保証されません。また、アプリ内のタイマーは、アプリの停止中には動きません。

エージェントに、そのリクエストで必要な処理は応答を返すまでに完了させ、応答の後に続ける処理はバックグラウンドタスクに投入し、定期的な処理は Keelson の定期実行ジョブへ移すよう依頼します。未処理の内容を DB に保存して定期実行ジョブで処理する方法もありますが、次の実行まで待ち時間が生じます。ブラウザ側のタイマーは、この制約の対象外です。詳しくはアプリが動くタイミングを参照してください。

  • スケジュールがプランの最小間隔より短くないか(デプロイ時に拒否されます)
  • 前回の実行が終わっていないか(重複実行はスキップされます)
  • 月間実行回数の上限に達していないか(当月はスキップ)
  • アプリがサスペンドされていないか
  • 「定期実行」タブの実行履歴で、スキップの理由を確認できます

更新したのに見た目が変わらない

Section titled “更新したのに見た目が変わらない”

ブラウザに古いファイルが残っている可能性があります。デプロイの完了を確認し、ページを強制再読み込みします(Mac: Cmd + Shift + R / Windows: Ctrl + Shift + R)。

静的サイトの更新は配信先へ順次反映されるため、完了後も少し時間がかかることがあります。変わらない場合は、更新先のアプリとアップロードしたファイルが合っているか、エージェントに確認を依頼します。

利用者が「アクセスできません」と言う

Section titled “利用者が「アクセスできません」と言う”
  1. ワークスペースのメンバーか(招待が未承諾でないか)
  2. ブロックされていないか
  3. アプリの閲覧権限があるグループに入っているか
  4. 登録されたアカウントでログインしているか
  5. IP 制御で拒否されていないか(403 画面に送信元 IP が出ます)

詳細はメンバーとロール。

「稼働枠が空いていない」(503)

Section titled “「稼働枠が空いていない」(503)”

「同時に使えるアプリ数」の上限です。直近 5 分にアクセスがあったアプリと、優先起動を設定したアプリが枠を使っています。予約していない枠を使うアプリへのアクセスが途絶えてから約 5 分待つか、重要なアプリに優先起動を設定するか、プランを上げます。

応答までに時間がかかりすぎた場合などに表示されます。Keelson では、応答開始まで 120 秒、ストリーミングを含む全体で 300 秒、ストリーミングの応答間隔で 120 秒の上限があります(HTTP の時間上限)。

エージェントに、時間のかかっている処理を調べ、処理を短くする・結果を少しずつ返す・定期実行に分ける方法を検討してもらいます。ストリーミングでも時間上限はなくなりません。

504 が表示されても、サーバー側では保存や送信が完了している場合があります。再操作する前に結果を確認します。実装では、同じ依頼を繰り返しても重複して保存・送信しない仕組み(冪等性)が必要です。

  • パスが /api/webhooks/ か /api/external/ で始まっているか
  • /api/external/ の場合、パスが auth.endpoints に宣言され、メソッドが宣言と合っているか(/api/webhooks/ は宣言不要で、methods も適用されません)
  • トークンのスコープと許可 IP が合っているか

外部システムとの連携。

シークレットを変えたのに反映されない

Section titled “シークレットを変えたのに反映されない”

シークレットは保存しただけでは、既に動いているアプリに反映されません。エージェントに反映まで依頼してください。自分で操作する場合は、コードも更新するならデプロイし、値だけを反映するならコンソールの「適用」を押します。「適用」は、コードを再アップロード・ビルドせずに再デプロイします(環境変数とシークレット)。

deploy.platform.error のようにプラットフォーム側のエラーが出た場合や、上記で解決しない場合は、お問い合わせから連絡してください。

次を添えると、やり取りの往復が減ります。

添えるもの取り方
ワークスペースの slugkeelson workspaces list --json の slug(URL は名前の変更前のものが残る場合があります)
アプリの slugkeelson.yaml の slug、または keelson apps list --workspace <ワークスペース slug> --json の slug
deploy_idkeelson status --json の出力、またはコンソールの「デプロイ」タブ
エラーの code と message と hintCLI の出力をそのまま。--json を付けるとまとめて取れます
起きた日時とタイムゾーンログを引くときに使います
利用者が開けない場合は、その人のメールアドレスと 403 画面に出た IP利用者が「アクセスできません」と言う

アプリのソースコードやシークレットの値は送らないでください。必要になれば、こちらから範囲を指定して依頼します。

料金・契約・請求書についての問い合わせも同じ窓口です(料金と契約の運用)。

ユーザーと権限


認証とログイン

Keelson では、社内アプリを利用できる人を、ワークスペースのメンバーのうち閲覧権限を持つ人に限定できます。利用者はアプリの URL を開き、Keelson のログイン画面でログインします。URL を知っているだけではアプリを開けません。

ログイン画面やログイン状態の管理は Keelson が用意するため、アプリ側で作る必要はありません。利用できる人の設定はチームにアプリを共有するで説明しています。

アプリの利用者は、次のいずれかでログインします。

  • Google アカウント
  • Microsoft アカウント
  • メールアドレス(ログイン用の使い捨てコードをメールで受け取る)

ワークスペースに登録されているメールアドレスでログインする必要があります。複数のアカウントを使い分けている場合は、登録されたアカウントを選んでください。

ワークスペースを作成する(コンソールにサインアップする)ときは、Google または Microsoft アカウントが必要です。

  1. アプリの URL を開きます。
  2. ログインしていなければ、Keelson のログイン画面が表示されます。
  3. Google / Microsoft アカウント、またはメールで受け取ったコードでログインします。
  4. 元のアプリに戻り、閲覧権限があればアプリが表示されます。

ログイン状態が保持されている間は、同じワークスペースの別のアプリも、ログインし直さずに開けます。それぞれのアプリの閲覧権限は必要です。

開けない場合はアクセスできないときの確認を参照してください。

ログインした人の情報をアプリで使う

Section titled “ログインした人の情報をアプリで使う”

アプリでは、ログインした人の名前を表示したり、その人のデータに絞って表示したりできます。AI エージェントには「Keelson が渡す利用者情報を使って、本人のデータだけを表示できるようにして」と依頼できます。

Keelson は、サーバー側のコードに利用者情報を渡します。その情報を使って、どのデータを表示・更新できるかを判断する処理はアプリ側で実装します。詳しくはログイン中のユーザー情報を使うを参照してください。

利用者情報の受け渡し(HTTP ヘッダー)

認証を通ったリクエストには、Keelson が利用者情報を HTTP ヘッダーという付加情報として付けます。主なヘッダーは次のとおりです。

ヘッダー内容
X-Keelson-User-Id利用者の ID
X-Keelson-User-Emailメールアドレス
X-Keelson-User-Name名前
X-Keelson-User-App-Permsそのアプリの権限

利用者が送信した同名のヘッダーは除去され、Keelson が確認した利用者情報に置き換えられます。アプリでは、この情報を使って利用者を判別します。ブラウザから送られた Authorization と Cookie も除去されます。

ブラウザからアプリを開くと、Keelson がログイン状態や権限を確認します。アプリの手前でこの確認を行う仕組みをプロキシと呼びます。

ブラウザ
│
▼
Keelson ① ログインしているか
② ワークスペースの有効なメンバーか
③ このアプリの閲覧権限があるか
④ IP 制御の条件を満たすか
│
▼
アプリ すべての条件を満たしたリクエストが届く
  • ①を満たさない場合は、ログイン画面へ移動します。
  • ②③を満たさない場合は、「アクセスできません」の画面になります。
  • ④を満たさない場合は、アクセスが拒否されたことを示す画面(HTTP 403)になり、接続元の IP アドレスが表示されます。

IP 制御は、接続元のネットワークでアクセスを制限する設定です。静的サイト・SPA・ハイブリッドアプリの静的ファイルには、IP 制御が適用されません。 ログイン・メンバー・閲覧権限の確認は行われます。詳しくはIP 制御を参照してください。

外部サービスからの通知(Webhook)や API 呼び出しでは、ブラウザでログインする代わりに、アプリトークンを使います。送信元が署名を付ける場合は、アプリ側でも署名を検証します。署名だけでは呼び出せません。

API 呼び出しを許可するパスとメソッドは、keelson.yaml の auth.endpoints に設定します。この設定は Webhook には適用されないため、Webhook の受信処理で許可するパスとメソッドを確認します。

AI エージェントには、連携したいサービスと受け取りたい通知・データを伝えて、設定と実装を依頼できます。詳しくは外部システムとの連携を参照してください。

メンバーとロール

アプリを一緒に使う人を、ワークスペースのメンバーとして追加できます。アプリを使う人、作って更新する人、メンバーや設定を管理する人に合わせて、役割を選びます。この役割を「ロール」と呼びます。

アプリを使うにはメンバーであることが必要です。利用できるアプリは、所属グループに割り当てられたアプリごとの権限で決まります。

アプリを使うだけの人には App User、作って更新する人には Developer を選びます。メンバーやワークスペースの設定を管理する人には Admin、請求や Owner の管理も担う人には Owner を割り当てます。

ロール主な用途・できること
Ownerワークスペースの責任者。Owner を含むメンバー管理、設定、請求を扱う
Adminチームの管理者。Owner 以外のメンバー管理とワークスペースの設定を扱う
Developerアプリを作ってデプロイ・更新する。メンバー管理やワークスペースの設定変更はできない
App User閲覧権限のあるアプリを使う。コンソールでの管理操作は行わない

Owner・Admin・Developer の人数は、プランの開発・管理用の人数枠(Developers 席)に数えられます。App User はこの人数枠に含まれず、人数無制限です。詳しくはプランと制限を参照してください。

ロールは「メンバー」画面から Owner / Admin が変更できます。Owner ロールの付与・変更は Owner だけができます。ワークスペースには最低 1 人の Owner が必要です。

方法参加するまでの流れ
招待Owner / Admin がメールアドレスを指定し、招待された本人がリンクを開いて招待を承諾する
参加 URL許可されたメールドメインの人が URL を開き、ログインして参加する。管理者の承認は不要
ワークスペースを探して参加申請(Discover)許可されたメールドメインの人が申請し、Owner / Admin が承認する

有効な招待が届いている場合、招待先と同じメールアドレスでログインしてアプリを開くと、招待が自動的に承諾されることもあります。参加が完了したかは、Owner / Admin が「メンバー」画面で確認できます。

メールドメインは、メールアドレスの @ より後ろの部分です。会社のメールドメインを許可すると、そのアドレスを持つ人が参加 URL や Discover を使えるようになります。設定はドメインと参加ポリシーで説明しています(Team プラン以上)。ドメイン自動参加で入ったメンバーのロールは App User です。

退職などでワークスペース内のすべてのアプリへのアクセスを止めたいときは、Owner / Admin が「メンバー」画面でそのメンバーを無効化します。異動などで利用できるアプリだけを変えたい場合は、所属グループやアプリの権限を変更できます。

  • 無効化がアクセス判定に反映されるまで、時間がかかることがあります。保存直後にすべてのアクセスが止まるとは限りません。
  • アカウントの削除ではありません。「再有効化」で戻せます。
  • 自分自身と、最後の Owner は無効化できません。Admin は Owner を無効化できません。

ロールに応じて、次のグループに自動的に所属します。これらはシステムグループと呼ばれ、所属メンバーを手動では変更できません。

ロール自動的に所属するグループ
Ownerowners developers everyone
Adminadmins developers everyone
Developerdevelopers everyone
App Usereveryone

新しいアプリには、everyone(全メンバー)に閲覧権限、developers(Owner / Admin / Developer)に管理権限が割り当てられます。管理権限には閲覧も含まれます。

アプリを利用・管理できるかは、このグループへの権限の割り当てで決まります。ロールを変更すると自動所属グループも変わるため、アプリへのアクセスにも影響します。特定のチームに利用を限定する手順はチームにアプリを共有するを参照してください。

利用者はまず、招待・登録されたメールアドレスでログインしているかを確認します。メンバーの状態は Owner / Admin に、アプリの権限はそのアプリを管理する人に確認を依頼できます。

  1. 登録されたアカウントでログインしているか — 複数の Google / Microsoft アカウントを使い分けている場合は、メールアドレスを確認します。
  2. メンバーになっているか — 「メンバー」画面で参加が完了しているかを確認します。まだ参加していなければ、本人に有効な招待リンクを開いて承諾してもらいます。
  3. 無効化されていないか — 「メンバー」画面の「無効化済み」で確認できます。
  4. アプリの閲覧・管理権限があるか — アプリ画面の「権限」で、その人の所属グループへの割り当てを確認します。
  5. IP 制御で拒否されていないか — IP 制御による拒否画面には、接続元の IP アドレスが表示されます。適用範囲はIP 制御を参照してください。

チームにアプリを共有する

アプリを使う人を特定のチームに限定して、URL を共有できます。このページでは、営業チームのメンバーと、アプリを管理する人が使えるように設定します。

招待とグループ作成には Owner / Admin、アプリの権限変更にはそのアプリの管理権限が必要です。全プランで利用できます。

ここで設定するのは、アプリを開ける人の範囲です。「自分の申請だけ見える」「上司だけ承認できる」といった業務上の権限は、アプリ側で実装します。

「メンバー」→「招待」で、営業チームのメールアドレスを入力し、ロールは App User を選びます。アプリを使うだけの人向けのロールで、開発・管理用の人数枠(Developers 席)には含まれません。

相手はメールのリンクを開き、招待されたアドレスと同じアカウントでログインして、招待を承諾します。詳細はメンバーを招待するを参照してください。すでに参加している人を再度招待する必要はありません。

有効な招待が届いている場合、招待先と同じメールアドレスでログインしてアプリを開くと、招待が自動的に承諾されることもあります。グループに追加する前に、「メンバー」画面でワークスペースへの参加が完了していることを確認します。

社員が多い場合は、Team プラン以上でドメイン自動参加を使うと、招待なしで参加できます。

グループは、アプリの権限をまとめて割り当てるためのメンバーの集まりです。「メンバー」→「グループ」で「営業」グループを作り、営業チームのメンバーを追加します。

グループには、表示名とは別に、設定やコマンドで指定する名前(キー)を付けます。この例では sales を使います。キーは作成後に変更できませんが、表示名とメンバーは変更できます。

3. アプリを使えるグループを設定する

Section titled “3. アプリを使えるグループを設定する”

アプリ画面の「権限」タブを開きます。閲覧権限はアプリを開いて使う権限、管理権限はアプリをデプロイしたり設定を変更したりする権限です。管理権限には閲覧も含まれます。

新しいアプリでは、everyone(全メンバー)に閲覧、developers(Owner / Admin / Developer)に管理が割り当てられています。「営業」に閲覧を追加するだけでは、everyone の閲覧が残り、全員が開ける状態が続きます。

  1. 「閲覧権限」の「編集」を押します。
  2. 「全員」(everyone)のチェックを外し、「営業」(sales)を選びます。
  3. 「閲覧権限」の「保存」を押します。

この例では、管理権限は developers のままにします。閲覧権限と管理権限は別々に保存するため、閲覧権限を変更したら、閲覧権限側で保存してください。

これで、営業メンバーと developers のメンバーがアプリを開ける設定になります。営業以外の App User は開けません。すでに権限を変更したアプリでは、ほかの閲覧・管理グループが残っていないかも確認します。

管理する人の範囲も変えたい場合は、グループとアプリ権限を参照してください。そのアプリの管理権限を持つ人は、閲覧グループに入っていなくてもアプリを開けます。

設定の保存後、アクセス判定への反映には時間がかかることがあります。ログイン中の利用者についても、反映後は変更した権限で判定されます。

アプリ画面の URL(https://<ワークスペース>--<アプリ>.keelson.run)をコピーして、チャットやメールで共有します。App User はコンソールでアプリの URL を確認できず、招待メールにもアプリの URL は含まれません。

「招待されたメールアドレスでログインしてください」と添えると、複数のアカウントを使う人にも伝わります。

5. 共有相手と対象外の人で確認する

Section titled “5. 共有相手と対象外の人で確認する”

共有したい人が開けることと、閲覧・管理のどちらの権限も持たない人が開けないことを確認します。

確認期待する結果
営業グループの利用者が URL を開くログイン後にアプリが表示される
営業グループに入っていないアプリ管理者が URL を開くログイン後にアプリが表示される
営業グループに入っておらず、ほかのグループ経由でも閲覧・管理権限を持たない App User が URL を開くログイン後に「アクセスできません」の画面になる
ログインしていない人が URL を開くKeelson のログイン画面になり、アプリは表示されない

対象外の利用者に確認を頼めない場合は、自分の別のメールアドレスを App User として招待し、営業グループに追加せずに試せます。

保存した設定は、アプリ一覧の「アクセス」欄で確認できます。全員に閲覧を許可していなければ人数が表示され、人数にカーソルを合わせるとグループ名が表示されます。実際に開けるかどうかは、上の手順で確認します。

症状確認すること
営業の人が開けないワークスペースへの参加が完了しているか、sales に所属しているか、登録されたメールアドレスでログインしているかを確認します
営業以外の人も開けるeveryone やほかの閲覧グループが残っていないか、その人が管理グループに所属していないかを確認します
保存した設定でアクセスが変わらない反映に時間がかかることがあります。少し待って再読み込みし、「権限」タブの保存内容と、確認に使ったアカウントを見直します

メンバーの無効化や IP 制御など、その他の確認項目はアクセスできないときの確認を参照してください。

AI エージェントに設定を依頼する場合

Section titled “AI エージェントに設定を依頼する場合”

グループ作成やアプリの権限設定は、CLI を使える AI エージェントにも依頼できます。対象のワークスペース、アプリ名、共有する人のメールアドレス、管理権限を残す人を伝えます。操作には、コンソールで設定する場合と同じ権限が必要です。

アプリ sales-tool を、営業チームとアプリ管理者が使えるように設定してください。ワークスペースへの参加が完了した [email protected] と [email protected] を「営業」グループ(キー: sales)に追加し、閲覧は sales、管理は developers に設定してください。設定後に、閲覧・管理を許可したグループを教えてください。

AI エージェントによる設定後も、手順 5 のアカウントごとのアクセスを確認します。

CLI で設定する例

ワークスペースへの参加が完了したメンバーを、作成したグループに追加します。次のコマンドは、コンソールで行う手順 2・3 と同じ設定です。コンソールで設定済みの場合は実行不要です。

Terminal window
keelson groups create sales --name "営業"
keelson groups members add sales [email protected] [email protected]
keelson access set --app sales-tool --view sales --manage developers

access set には、残したい閲覧・管理グループを指定します。この例では閲覧を sales、管理を developers に設定します。

アプリの中で「本人の分だけ」にする

Section titled “アプリの中で「本人の分だけ」にする”

ここまでの設定で、アプリを開ける人の範囲を決められます。さらに「自分の申請だけを見せたい」場合は、AI エージェントに次のように依頼できます。

Keelson が渡すログイン中の利用者情報を使い、本人の申請だけを表示してください。他人の申請の URL や ID を指定しても、取得・更新できないようにし、その確認も行ってください。

利用者ごとの画面表示に加えて、データを取得・更新するサーバー側でも権限を確認する処理が必要です。AI エージェントには、別の利用者のデータを取得・更新できないことをどう確認したかも報告してもらうと、実装結果を確認できます。

判定には、Keelson が付けた利用者 ID(X-Keelson-User-Id)やグループ情報を使います。ブラウザから送られた値だけで本人と判断しないようにします。実装はログイン中のユーザー情報を使うを参照してください。

ワークスペース・アプリ・業務上の権限の違い
権限設定する人・場所例
ワークスペースの管理権限(ロール)Owner / Admin が付与。Owner の付与・変更は Owner のみメンバーを招待できるか、請求を扱えるか
アプリの閲覧・管理権限(view / manage)そのアプリの管理権限を持つ人が、グループに割り当てるアプリを開けるか、デプロイ・設定変更ができるか
アプリ内の業務上の権限アプリ側で実装する自分の申請だけ見られるか、上司として承認できるか

アプリの管理権限は、デプロイや設定変更を行う人のための権限です。業務上の承認者と同じ人とは限りません。ロールの詳細はメンバーとロールを参照してください。

ワークスペースと契約


メンバーを招待する

作ったアプリを同僚に使ってもらうときは、メールアドレスを指定してワークスペースに招待します。アプリを使うだけの人と、作成や更新も行う人で、招待時に選ぶロールが異なります。

招待は Owner または Admin が行えます。全プランで利用できます。相手が招待を受け入れ、「メンバー」画面で参加を確認できれば完了です。

アプリを作る・管理する人の人数枠を「Developers 席」と呼びます。App User はこの枠を使いません。

招待する相手ロールDevelopers 席
アプリを使う人App User消費しない
アプリを作ってデプロイする人Developer消費する
メンバー管理も任せる人Admin消費する
ワークスペースの共同責任者Owner消費する

Admin は Owner ロールで招待できません。Developer と App User は他のユーザーを招待できません。

Developer 以上のロールで招待すると、招待を出した時点で Developers 席を 1 つ確保します(相手が招待を受け入れる前でも)。席が上限に達していると、これらのロールでは招待できません。App User は席に関係なく何人でも招待できます。

  1. コンソールの「メンバー」を開き、「メンバーを招待」を選びます
  2. メールアドレスを入力します(1 回に最大 50 件)
  3. ロールを選びます
  4. 「招待を送信」を選びます

招待された人に、リンク付きのメールが自動で送られます。送信直後の結果画面では、招待リンクをコピーして直接共有できます。結果画面を閉じた後にリンクが必要になった場合は、「メンバー」画面の「招待を再送」で新しいリンクを発行して共有します。

有効期限は作成から 3 日間(72 時間) です。

  1. メールのリンクを開きます
  2. 招待されたメールアドレスと同じ Google / Microsoft アカウント、またはメールのワンタイムコードでログインします

ログインすると、招待が自動的に受け入れられます。招待されたアドレスで既にログインしている場合は、メールのリンクを開いた時点で参加します。

別のメールアドレスでログインしている場合は、招待されたアドレスでログインし直します。

参加後に使えるアプリは、アプリごとの権限で決まります。既定では全メンバーが全アプリを使えます。

招待した人は、使ってほしいアプリの URL をチャットやメールで相手に送ります。招待メールには、アプリの URL は含まれません。

App User はコンソールでアプリの一覧や管理画面を利用できません。参加後の画面には、アプリの URL を招待した人に確認する案内が表示されます。

「メンバー」画面では、招待を受け入れる前の人は「保留中」、参加した人は「アクティブ」と表示されます。

  • 取り消し — まだ受け入れられていない招待を無効にします
  • 再送 — まだ受け入れられていない招待に新しいリンクを発行します。古いリンクは無効になり、有効期限は再送時から 3 日間(72 時間)です
  • 期限切れ・取り消し済みの招待は、同じアドレスで新しく作り直します
症状確認すること
招待が作れないOwner / Admin か。既に参加済みでないか。Developer 以上なら席が空いているか
メールが届かない迷惑メールフォルダを確認。届かなければ送信直後の結果画面でリンクをコピーして共有。画面を閉じた後は「招待を再送」で新しいリンクを発行
招待を受け入れられない招待されたアドレスでログインしているか。期限切れ・取り消しになっていないか
参加したのにアプリが開けないアプリの閲覧権限、ログイン中のアカウント、IP 制御

自社ドメインのユーザーを招待なしで参加させるには、ドメインと参加ポリシーを設定します(Team 以上)。

グループとアプリ権限

「経理のアプリは経理チームに」「社内のお知らせは全員に」のように、アプリを使える人をグループで指定できます。アプリの更新や設定変更を行う人も、別に指定できます。

  • 必要な権限 / プラン: グループの作成・メンバー変更は Owner / Admin。アプリの権限変更は、そのアプリの manage を持つ人。全プラン
  • 完了条件: 利用を許可した人のアカウントでアプリを開け、閲覧・管理のどちらの権限も持たない人のアカウントでは「アクセスできません」と表示される

目的別の手順はチームにアプリを共有するにあります。

権限できること
閲覧(view)実行中のアプリを開いて使う
管理(manage)アプリのデプロイ、設定変更、シークレット編集、権限の変更。閲覧も含む

権限はユーザー個人ではなくグループに割り当てます。グループのメンバーを変えると、そのグループを割り当てたアプリの利用者・管理者も変わります。

ロール(Owner / Admin / Developer / App User)は、ワークスペースで行える操作を決めます。アプリを使えるかどうかは、上の閲覧・管理権限で決まります。ただし、IP 制御の設定は Owner / Admin が行い、アプリの管理権限だけでは変更できません。

アプリを作ると、次が自動で割り当てられます。

グループ権限
everyone(全メンバー)閲覧
developers(Owner / Admin / Developer)管理

つまり何もしなければ、ワークスペースの全員がアプリを使え、開発者ロールの人が管理できます。絞りたいアプリだけ変更します。

特定のグループだけに絞るときは、everyone の閲覧を外す必要があります。 「経理」グループに閲覧を足しただけでは、everyone の閲覧が残っているので全員が開けたままです。また manage には閲覧が含まれるので、管理グループ(既定では developers)のメンバーは閲覧グループに入っていなくても開けます。

「メンバー」→「グループ」で管理します。

システムグループ — ロールから自動的に決まります。編集できません。

キーメンバー
ownersOwner
adminsAdmin
developersOwner / Admin / Developer
everyone全メンバー

admins に Owner は含まれません。Owner と Admin の両方に管理を任せる場合は、owners と admins を指定します。

カスタムグループ — 「経理」「営業」「店舗スタッフ」など、業務の単位で作ります。キー(経理 のような識別子。日本語も可)は作成後に変更できません。表示名とメンバーは変更できます。アプリに割り当てられている間は削除できません。

アプリ画面の「権限」タブで、閲覧グループと管理グループを選びます。

  • 管理グループは最低 1 つ必要です(空にはできません。メンバーがいないグループだけにもできません)
  • 自分の管理権限を外す操作は、他の管理グループに有効なメンバーがいる場合でも確認を求められます
  • 保存した変更は、ログイン中のユーザーも含め、通常 2 分以内に反映されます
やりたいこと設定
全員が使える社内ツール既定のまま
経理チームとアプリを管理する開発者が使える閲覧: keiri、管理: developers
利用は全員、設定変更は Owner / Admin だけ閲覧: everyone、管理: owners と admins

「上司だけが申請を承認できる」「部署ごとに見せる情報を変える」といったルールは、アプリ側に実装します。アプリを更新するための管理権限とは別に、誰がどの情報を見たり変更したりできるかを決めます。

AI エージェントには、たとえば「経理グループだけが経費の一覧を見られるようにして」と依頼します。作成後は、対象のグループの人と、それ以外の人で表示や操作を確認します。

実装では、部署などのグループは attributes.groups、アプリの管理権限は X-Keelson-User-App-Perms の manage で確認できます。詳しくは認証情報をアプリで使うを参照してください。

アプリ台帳の「アクセス範囲」に、全員に公開されていれば「全員」、限定されていれば対象の人数とグループが表示されます。

AI エージェントに設定を依頼するときは、グループに含める人と、アプリを使う人・管理する人を伝えます。次は Owner / Admin が、経理グループに閲覧を許可し、Owner / Admin に管理を任せる CLI での操作例です。

Terminal window
keelson groups list
keelson groups create keiri --name "経理"
keelson groups members add keiri [email protected] [email protected]
keelson access show --app my-app
keelson access set --app my-app --view keiri --manage owners --manage admins
keelson access set --app my-app --view none # 閲覧を空にする。管理権限を持つ人は利用可能

ドメインと参加ポリシー

社員が多い場合は、同じ会社のメールアドレスを持つ人が、個別の招待メールなしで参加できるようにすると便利です。社内に参加リンクを配る方法と、コンソールの「ワークスペースに参加 / 作成」画面から申請してもらう方法があります。

設定と申請の承認は Owner または Admin が行います。Team プラン以上で利用できます。

参加方法メンバーになるタイミング
社内で共有された参加リンクを開く対象のメールアドレスでログインし、「参加」を選んだとき。管理者の承認は不要
「ワークスペースに参加 / 作成」画面から申請するOwner / Admin が申請を承認したとき

どちらの方法でも、参加時のロールは App User です。Owner / Admin が後からロールを変更できます。

参加できる人は、メールアドレスの @ より後ろの部分(メールドメイン)で決まります。たとえば [email protected] のドメインは example.co.jp です。

  • 設定する Owner / Admin と、ワークスペース作成者のメールドメインが一致している必要があります
  • Gmail や Outlook.com などのメールドメインでは、参加リンクを有効にできません
  • 同じドメインを複数のワークスペースで利用できます(部署ごとのワークスペースなど)

参加リンクを有効にすると、操作した Owner / Admin のメールドメインが自動で登録されます。事前に手動で追加する必要はありません。登録内容は「設定」→「セキュリティ」→「アクセス制御」で確認できます。手動で追加する場合も、自分とワークスペース作成者のメールドメインに一致するものだけを登録できます。

参加リンクを有効にして共有する

Section titled “参加リンクを有効にして共有する”
  1. 「設定」→「セキュリティ」→「チーム参加リンク」を開きます
  2. 参加リンクを有効にします
  3. URL をコピーして社内のチャットや案内ページで共有します

対象のメールアドレスを持つ人がリンクを開き、ログインして「参加」を選ぶと、その場でメンバーになります。「メンバー」画面で参加を確認できます。

参加リンクを有効にすると、対象のメールドメインでログインした人の「ワークスペースに参加 / 作成」画面にもワークスペースが表示されます。この画面で「参加をリクエスト」を選ぶと、Owner / Admin に申請が届きます。

申請は「設定」→「セキュリティ」の「保留中の参加リクエスト」から承認または却下します。申請しただけではメンバーにならず、承認されるまでアプリにアクセスできません。

参加後にアプリの URL を共有する

Section titled “参加後にアプリの URL を共有する”

参加リンクは、ワークスペースに参加するための URL です。使ってほしいアプリの URL は別途共有します。App User にはコンソールのアプリ一覧が表示されないためです。

参加後に使えるアプリは、アプリごとの権限で決まります。

  • 「リセット」で現在の URL を無効にし、新しい URL を発行できます。共有先を見直したいときに使います
  • 参加リンクを無効にすると、個別の招待で参加する方式に戻ります。既に参加したメンバーには影響しません

設定の詳細では、参加方法を「参加ポリシー」と呼びます。参加リンクの有効・無効に連動するため、ポリシーを直接選ぶ操作はありません。

ポリシー動作
招待のみ(既定)Owner / Admin の個別の招待で参加する
参加リンク対象のメールドメインの人は参加リンクから承認なしで参加できる。「ワークスペースに参加 / 作成」画面から申請した場合は管理者の承認が必要

IP 制御

アプリをオフィスや会社の VPN からだけ使えるようにします。利用者のログインとアプリの権限に加えて、接続元のネットワークを確認します。この仕組みを IP 制御と呼びます。

対象は、サーバー側で動く Web アプリと、画面の配信と処理を分けたハイブリッドアプリの API 部分です。静的サイト、SPA、ハイブリッドアプリの静的ファイル、Media の配信 URL には適用されません。 詳しくは適用範囲を参照してください。

  • 必要な権限 / プラン: アクセス元の作成、ワークスペースのデフォルト、アプリごとの設定のいずれも Owner / Admin。アプリの manage 権限だけでは変更できません。全プラン
  • 完了条件: 許可したネットワークからはアプリが開け、それ以外からはアクセスできない旨の画面(接続元の IP アドレスが表示される)になる

IP 制御は「アクセス元」という単位で設定します。アクセス元は、許可するネットワークのアドレスを、名前を付けてまとめたものです。たとえば「東京オフィス」という名前で、複数の IP アドレスやアドレスの範囲を登録できます。範囲の指定には、203.0.113.0/24 のような CIDR という書き方を使います。

  1. ワークスペースでアクセス元を作る
  2. ワークスペースの「デフォルトで許可するアクセス元」を選ぶ(空なら IP 制限なし)
  3. アプリごとに「ワークスペースのデフォルトを継承」か「このアプリ専用のアクセス元」かを選ぶ

新しいアプリはデフォルトを引き継ぎます。ワークスペースのデフォルトを変更すると、それを引き継ぐアプリにまとめて適用できます。専用のアクセス元を設定したアプリには、その専用設定が使われます。

アクセス元を作る(Owner / Admin)

Section titled “アクセス元を作る(Owner / Admin)”

コンソールの「設定」→「セキュリティ」→「アクセス元の管理」で「アクセス元を登録」を選びます。

  • 名前 — 「福岡オフィス」「VPN」など
  • IP / CIDR — 1 行に 1 つ。IPv4 / IPv6 両方に対応。単一 IP(203.0.113.10)も範囲(203.0.113.0/24)も書けます。「現在の IP を追加」で自分の送信元を入れられます
  • ワークスペースのデフォルトに追加する — チェックすると、作成と同時にデフォルトに含まれます

登録済みのアクセス元をデフォルトに設定する場合は、同じ「セキュリティ」画面の「デフォルトで許可するアクセス元」で選び、「デフォルトを保存」を選びます。

ネットワーク管理者には「このアプリをオフィスと会社の VPN から利用できるようにしたい」と伝え、登録する固定 IP アドレスやアドレス範囲を確認します。

自宅や携帯回線では IP アドレスが変わることがあり、登録後にアクセスできなくなる場合があります。

アプリごとの設定(Owner / Admin)

Section titled “アプリごとの設定(Owner / Admin)”

アプリ画面の「設定」→「セキュリティ」で選びます。

モード動作
ワークスペースのデフォルトを継承デフォルトのアクセス元を適用する。デフォルトが空なら IP 制限なし
このアプリ専用のアクセス元デフォルトを置き換え、選んだアクセス元(1 つ以上)だけを適用する

IP 制限をかけないアプリは、空のデフォルトを引き継ぐ設定にします。デフォルトを空にすると、それを引き継ぐすべてのアプリで IP 制限が外れます。 他のアプリの制限を維持する場合は、先にそれらを「このアプリ専用のアクセス元」に切り替えてから、デフォルトを空にしてください。

設定はデプロイなしで反映されます。通常は 2 分以内に反映されます。障害中の変更は復旧後に反映されます。

許可されていないネットワークからブラウザで開くと、ログイン後に「このアプリは、許可されたアクセス元からのみ利用できます」という画面と、現在の接続元の IP アドレスが表示されます。

会社の VPN への接続が必要かを確認します。アクセス元への追加が必要な場合は、表示された IP アドレスを Owner / Admin に伝えます。

  • 対象は、コンテナで動く Web アプリと、ハイブリッドアプリの API 部分への、ログインユーザーのアクセスです
  • 静的サイト、SPA、ハイブリッドアプリの静的ファイル、Media の配信 URL(/__keelson/media/<ファイルID>)には適用されません。Media ではログインとアプリの閲覧権限が確認されます(ファイルとメディア)
  • アプリトークンと Webhook には、ワークスペースやアプリの設定ではなく、トークンごとの許可 IP が適用されます(外部システムとの連携)
  • AI クライアントから MCP 経由で呼ばれるツールにも、同じワークスペースのデフォルトとアプリごとの設定が適用されます。判定されるのは AI クライアントのサーバーの IP アドレスです。claude.ai や ChatGPT からの呼び出しは利用者の PC ではなく各社のサーバーから届くため、IP 制限のあるアプリではそのままでは拒否されます(AI クライアントから使う(MCP))

判定は ログイン → アプリの権限 → IP の順で行われます。IP 制御は、ログインとアプリの権限確認に追加される制限です。

許可されていないネットワークからのアクセスには HTTP 403 を返します。ブラウザでページを開く場合は案内画面を、ページ内の通信(fetch / XHR)や API 呼び出しには JSON を返します。

プランと制限

プランを選ぶときや、アプリの追加・更新ができなくなったときに、利用上限を確認できます。料金は料金ページ、プラン変更・解約・請求書などの操作は料金と契約の運用を参照してください。

まず、次の 3 つを確認すると、必要なプランを選びやすくなります。

  • 同時に使えるアプリ数 — 同時に利用する Web アプリの数です。保存してあるアプリの総数とは異なります
  • アプリを作る・管理する人数(Developers) — Owner / Admin / Developer の人数枠です。アプリを使うだけの App User は数えません
  • ストレージ — 保存したファイルなどの合計容量です。データベースの容量は別に扱います
StarterPlusTeam
同時に使えるアプリ数124
Developers(Owner / Admin / Developer の席)123
バックグラウンド実行 実行回数 / 月5002,5005,000
バックグラウンド実行 合計実行時間 / 月300 分1,500 分5,000 分
定期実行ジョブの設定数 / アプリ3510
定期実行できる最短の間隔60 分15 分5 分
バックグラウンドタスクの宣言数 / アプリ101010
1 回の実行時間の上限(定期実行・タスクの試行)3 分5 分10 分
ビルド回数 / 月100 回300 回1,000 回
ビルド時間 / 月500 分1,500 分5,000 分
同時ビルド数112
日次バックアップの保存数1 世代3 世代7 世代
指定した時刻に復元できる期間(PITR)24 時間7 日14 日
ストレージ(ワークスペース合計)10 GB20 GB50 GB
データベース容量(DB ごと)固定上限なし固定上限なし固定上限なし
メール送信の宛先数 / 月5002,00010,000
優先起動(有料契約中)—○○
カスタムドメイン——最大 10 件
メールの独自送信ドメイン——最大 10 件(許可アドレスはドメインごとに 20 件まで)
アクセスログの閲覧範囲30 日90 日180 日
アプリログの閲覧範囲7 日14 日30 日

Enterprise プランは準備中です。上記を超える要件はお問い合わせください。

アプリを実行できる形に準備する処理を「ビルド」、決まった時刻や間隔で動かす処理を「定期実行ジョブ(cron)」、アプリが SDK で投入したときに tasks に宣言したコマンドを別のインスタンスで動かす処理を「バックグラウンドタスク」と呼びます。タスクは開始した試行 1 回(再試行を含む)をバックグラウンド実行の 1 回と数えます。バックアップの「1 世代」は、保存されたバックアップ 1 回分です。

Email SDK によるメール送信の宛先数は、To・CC・BCC の宛先 1 件を 1 と数えます。この上限はワークスペース全体の合計とアプリごとの合計の両方に適用され、どちらかに達すると送信できなくなります。

  • アプリの利用者数(App User)は無制限
  • 有料プランではアプリの保存数は無制限。上限がかかるのは「同時に動かせる数」だけです。トライアル中は保有数と同時に動かせる数にそれぞれ上限があります(下の「トライアル中の制限」を参照)
  • 認証・アクセス制御・IP 制御
  • 日次バックアップ、手動スナップショット(1 アプリ 5 回/日)、PITR、バックアップのダウンロード
  • Managed SQLite(アプリ / ワークスペース単位で DB を分離)
  • 14 日間の無料トライアル(1 アカウント 1 回)。Starter / Plus はカード登録なしで開始できます
  • 静的サイトとしてデプロイ済みのアプリを除き、作成できるアプリは 3 個までです。未デプロイのアプリやサーバーを使うアプリはこの上限に数えます。4 個目の作成は拒否されます
  • 同時に動かせるアプリは、プランを問わず 3 個までです。トライアル中はこの上限がプランの稼働アプリ枠となり、追加アプリのアドオンも加算されません。そのため Team は 4 個から 3 個に減ります
  • ビルドは直近 24 時間で 7 回までです。日付が変わっても一斉にはリセットされません
  • ビルドはトライアル期間の通算で 30 回までです。この 30 回は通算のため、月が変わってもリセットされません
  • ビルド時間と同時ビルド数は、ご契約プランの値がそのまま適用されます
  • 有料プランへ移行すると、保有アプリ数の上限は外れ、稼働アプリ枠はプランの値と追加アプリのアドオンを合わせた数に戻ります
上限挙動
同時に使えるアプリ数空き枠が必要なアプリの起動・デプロイができなくなります。稼働中のアプリには影響しません。枠が空いていない状態でスリープ中のアプリにアクセスすると、起動できないことを知らせる案内ページ(HTTP 503)が表示されます。優先起動を設定していないアプリの枠は、最後のアクセスから約 5 分で解放されます
Developers開発者の席を使う招待や、新たに席を使うロール変更ができなくなります。App User の招待や、使用する席数を増やさないロール変更はできます
バックグラウンド実行 実行回数当月の残りの定期実行はスキップされ、タスクの新規投入は拒否されます(TASK_MONTHLY_QUOTA_EXCEEDED)。翌月にリセットされます
バックグラウンド実行 合計実行時間当月の残りの定期実行はスキップされ、タスクの新規投入は拒否されます(TASK_MONTHLY_QUOTA_EXCEEDED)。すでに開始した実行・試行は完了まで継続し、上限は翌月にリセットされます
定期実行ジョブの設定数・間隔・実行時間、タスクの宣言数・実行時間keelson.yaml がプランに合わない場合、デプロイできません。AI エージェントに、表示された上限に合わせて設定の見直しを依頼します
ビルド回数 / ビルド時間(月間)新しいビルドが拒否されます。どちらの枠も翌月にリセットされます。トライアル中の通算 30 回はリセットされないため、続けるには有料プランへの移行が必要です
同時ビルド数上限に達している間、実行中のビルドが終わるまで新しいビルドは拒否されます
メール送信の宛先数当月の残りの送信は拒否されます(MONTHLY_QUOTA_EXCEEDED)。翌月にリセットされます
メールの独自送信ドメインドメインや許可アドレスをそれ以上追加できません。使わなくなったものを先に削除してください
ストレージすぐには停止しません。超過時はサポートから連絡します
データベース容量プランごとの固定上限はありません。ただし、基盤側の技術上限は適用されます。また、平常の業務利用を著しく超える負荷や、プラットフォームの安定運用・他のお客様の利用・当社の合理的な運用コストに影響を及ぼす利用を検知した場合、アプリの実行を制限または停止することがあります。緊急時は事前の通知なく制限することがあります

有料契約中は、必要な枠や容量を追加できます。追加の操作や請求は料金と契約の運用を参照してください。トライアル中は追加できません。

アドオン単位
追加ストレージ+50 GB
同時に使えるアプリ数の追加+1 枠
追加 Developers+1 席

Web アプリが起動・利用のために確保する枠の数です。通常のアプリは、最後のアクセスから約 5 分で枠が自動的に解放されます。アクセスが続いている間は枠を使います。

  • スリープ中・サスペンド中のアプリは枠を消費しません
  • 定期実行ジョブだけのアプリは枠を消費しません(Web アプリとは別に実行されます)
  • 静的サイトは枠を消費せず、作成できる本数にも上限はありません
  • 優先起動を設定したアプリは、アクセスがなくても枠を常に予約します。有料契約中は Plus 以上が対象で、設定できる本数はアプリ枠の合計より 1 本少ない数までです。トライアル中は全プランで 2 本まで設定できます

起動と停止の仕組みはアプリが動くタイミングを参照してください。

アプリを作る・管理する人の人数枠を「Developers 席」と呼びます。Owner / Admin / Developer のメンバーが 1 人につき 1 席を使います。App User は数えません。

Owner / Admin / Developer の招待は、相手が受け入れる前でも、有効期限内は 1 席を予約し、使用量に含まれます。不要になった招待は取り消すと予約が外れます。

操作によって異なる席数の数え方

Owner / Admin / Developer として新しく招待するときは、これらのロールの参加済みメンバーと、有効期限内の未受諾の招待を合計して上限を判定します。招待の受諾時や、新たに開発者の席を使うロール変更では、これらのロールの参加済みメンバーだけを数えます。既に席を使っているメンバーのロール変更など、席数が増えない変更はこの上限で制限されません。

デプロイ用に保存したアプリのファイル(アーティファクト)、Files / Media SDK のファイル、スナップショットの合計容量です。ワークスペース全体で集計し、ログは含みません。Managed SQLite の容量はこの集計枠とは別に管理され、プランごとの固定上限はありません。

画面の表示やデータの保存など、アプリに送る 1 回のリクエストには時間の上限があります。長い処理がある場合は、AI エージェントに上限内で完了する処理方法を相談できます。

項目上限
応答を開始するまで(最初のレスポンスヘッダ)120 秒。超えるとタイムアウト(HTTP 504)
1 リクエストの合計(結果を少しずつ返すストリーミングを含む)300 秒
ストリーミングで次のデータを返すまでの間隔(チャンク間隔)120 秒

タイムアウトが表示されても、アプリ側では保存などの処理が完了している場合があります。AI エージェントには「同じ操作を繰り返しても、申請や注文が二重に登録されないようにして」と依頼してください。

技術的には、HTTP 504 が返ってもアプリ側の処理が取り消されたとは限りません。Keelson は POST リクエストを自動再送しませんが、利用者やクライアントが再送する可能性があります。そのため、同じリクエストを繰り返しても結果が重複しない実装が必要です。

定期実行とバックグラウンドタスクの時間上限は上のプラン別一覧、ビルドの時間上限は keelson.yaml リファレンスを参照してください。

料金と契約の運用

プランを変更したいとき、支払い方法や請求書を確認したいとき、Keelson の利用を終了したいときの手順を案内します。各プランの数値上限はプランと制限、料金は料金ページを参照してください。

  • 必要な権限: Owner または Admin。ワークスペースの削除は Owner だけ
  • 場所: コンソールの「設定」→「課金と使用量」。ワークスペースの削除は「設定」→「危険な操作」
  • 各アカウントは 1 回だけ 14 日間の無料トライアルを使えます。Starter / Plus はカード登録なしで開始でき、終了までにカードを登録しなければ課金されずに自動終了します。Team はトライアル開始時にカード登録が要ります(期間中に解約すれば課金なし)
  • カード登録済みの場合、トライアル終了日が初回請求日になり、以降は契約日ベースで毎月請求されます
  • トライアルを使い切ったアカウントが 2 つ目のワークスペースを作る場合は、決済完了と同時に課金が始まります
  • 通貨は請求先の国で決まります(日本は JPY 税込、それ以外は USD 税抜)。初回契約で固定され、後から変えるにはサポートへの連絡が要ります
  • 請求書払い・銀行振込は未対応です

利用者(App User)の人数は料金に影響しません。

トライアル中の制限と終了時の挙動

Section titled “トライアル中の制限と終了時の挙動”

トライアル中は、保有アプリ数・同時に動かせるアプリ数・ビルド回数に、プラン共通の上限があります。具体的な数値と数え方は、トライアル中の制限で確認できます。

終了時の状態起きること
カード登録済みそのまま有料プランに移行し、初回請求が発生する。保有アプリ数の上限は外れ、稼働枠はプランの値に戻る
カード未登録(Starter / Plus)課金されずに自動終了する。以後、新しいデプロイ・アプリの起動・定期実行はブロックされる。アプリとデータはすぐには削除されない(解約後のアプリとデータ)

トライアル中のプラン変更は、アップグレード・ダウングレードとも直ちに適用され、料金は発生しません。

「課金と使用量」→「プラン変更」から選びます。確定前に金額が表示されます。

変更適用請求
アップグレード直ちに現在のプランの未使用期間分を差し引き、新しいプランの残り期間分を変更時に請求する。次回の請求日からは新プランの満額
ダウングレード現在の請求期間の終わりに日割り精算はしない。それまでは現在のプランのまま使える

ダウングレードは予約として表示され、「予約を取り消す」で切り替え前なら取り消せます。

現在の使用量が変更先の上限を超えている場合は警告が出ます。超えたまま切り替えると、次のようになります。

  • 同時に使えるアプリ数: スリープ中のアプリは、使用量が上限内に減るまで起動できない
  • Developers 席: 上限内に収まるまで、Developer 以上のロールでの招待や、App User から Developer 以上への変更ができない
  • 定期実行の回数: 当月の残りはスキップされる
  • ストレージ: 超過してもすぐには止まらない「ソフト上限」です。デプロイ時に警告が出て、サポートから連絡します。継続的に大きく超過している場合は制限の対象になります
  • カスタムドメイン(Team のみ): 登録済みのものは残るが、追加・再登録ができない

アドオンを増減する(有料契約中の Team 以上)

Section titled “アドオンを増減する(有料契約中の Team 以上)”

「課金と使用量」→「アドオン」で、追加アプリ枠(+1)、追加 Developers 席(+1)、追加ストレージ(+50 GB)を数量で指定します。トライアル中は変更できません(トライアル終了後に追加できます)。増やした分は、残りの契約期間に応じた金額が登録済みの支払い方法へすぐに請求されます。減らした場合の差額は次回以降の請求に充当されます。

「課金と使用量」の「請求書」に最近の請求書が並び、各行から Stripe の請求書ページと PDF を開けます。

支払い方法の変更、請求先情報の編集、過去の請求書の一覧は、「課金を管理」ボタンから開く決済サービス Stripe の契約管理画面(カスタマーポータル、別タブ)で行います。ポータルで解約もできますが、Keelson のコンソールから解約する方が、終了日と影響を確認したうえで進められます。

決済に失敗した場合(支払い遅延)は、Stripe が再試行する間、サービスは通常どおり使えます。再試行が終わっても支払われないと契約が停止し、新しいデプロイ・アプリの起動・定期実行がブロックされます。「支払い方法を更新」から更新してください。

利用を終了する場合は、契約が終了するまでに、必要なデータを取り出します。 ワークスペースやアプリを削除する場合は、削除操作の前に行います。解約の申し込み後も契約終了日までは利用できるため、その間に準備できます。

データ取り出し方
Managed SQLite のデータコンソールのアプリ画面「復元ポイント」から、復元可能な期間内の時点を選んでダウンロード。データを書き出したファイル(SQLite の dump)を取得できます。契約終了後の猶予期間中もダウンロードできます
Files SDK のファイルアプリにファイル一覧とダウンロード処理を用意し、アプリが動いているうちに取り出します
Media SDK のファイルアプリに保存したファイル ID からリンクを用意し、そのアプリを閲覧できるメンバーがログインして取り出します
ソースコードGit。または「デプロイ」タブの各デプロイからダウンロード
環境変数とシークレットシークレットの値はコンソールに表示されません。必要なら元の管理場所から控えます
アクセスログ・アプリログ「ログ」タブからエクスポート(アプリログは最大 5,000 行)

Files / Media には、コンソールや CLI からファイルを一覧表示・ダウンロードする機能がありません。AI エージェントには「保存したファイルを一覧から取り出せる画面を作って。Files はダウンロード処理を、Media は保存済みの ID からリンクを用意して。アプリの閲覧権限がある人だけが使えるようにして」と依頼できます。取り出したファイルが開けることも確認します。使い分けはファイルとメディアで説明しています。

契約終了後はアプリを起動できなくなるため、Files / Media の取り出しは契約終了までに済ませます。

「課金と使用量」→「プランをキャンセル」から、終了日と影響を確認し、「キャンセルを確定」を選びます。

  • 契約終了日時(現在の請求期間の終わり)まではプランが有効なままです。解約の申し込み後もその日時まで利用でき、残り期間分の返金もありません
  • 契約終了日時までは「再開」でいつでも解約の申し込みを取り消せます
  • 契約終了日時を過ぎると、新しいデプロイ・アプリの起動・定期実行がブロックされます。アプリやデータはすぐには削除されず、削除の予告と猶予期間があります

契約が終了した(解約の申し込み後に契約終了日時を過ぎた、トライアルがカード未登録で終了した)場合や、未払いで契約が停止した場合、ワークスペースのアプリは起動できなくなりますが、すぐには消えません。

  1. 対象のアプリが見つかった時点で、Owner / Admin にコンソールとメールで削除の予告が届きます。予告には削除予定日が書かれています
  2. 一度も支払いの無いワークスペースの猶予は 3 日、支払い実績のあるワークスペースは 30 日です。猶予中も利用制限は続きますが、コンソールにログインして DB のバックアップをダウンロードできます
  3. 猶予中に契約が有効になると、予告は取り消され、アプリはそのまま残ります。契約が終了している場合は、プランを選び直して再契約します。未払いで停止している場合は、「支払い方法を更新」から未払いを解消します。アプリを残して使い続けたい場合は、この期間内に手続きを済ませます
  4. 猶予後、アプリは自動で削除されます。削除後の扱いは利用者が承認した削除と同じで、DB バックアップ・Files・秘密情報を含まない設定の記録が、通常の利用から切り離された状態で 7 日間保管されます。復元は保証されません

DB のバックアップは猶予期間中も取り出せます。Files / Media は契約終了までに取り出す必要があります(データの持ち出し)。

削除予定日は予告で確認できます。契約終了後のデータの取り扱いと、終了までのエクスポートについては、利用規約の第20条もご確認ください。

「設定」→「危険な操作」→「このワークスペースを削除」。Owner だけが実行でき、ワークスペース名を入力して確認します。

  • ワークスペースとすべてのアプリが削除され、メンバーはアクセスできなくなります。コンソールから削除を取り消すことはできません
  • 各アプリの DB バックアップ・Files・秘密情報を含まない設定の記録は、通常の利用から切り離された状態で 7 日間保管されますが、復元は保証されません
  • 削除するとログアウトします
  • 有効な契約がある場合、削除時に契約のキャンセル処理も行われるため、先に解約する必要はありません。ただし、キャンセル処理に失敗する場合があるため、削除後に請求が続いていないことを Stripe のポータル(削除前に「課金を管理」から開けます)または請求書メールで確認してください

Keelson のアカウント自体は、コンソール右上のアカウントメニュー →「Account settings」→「Security」の Delete account から自分で削除できます。削除されるのは Keelson のアカウントで、ログインに使っていた Google / Microsoft のアカウントは削除されません。所属していたワークスペースのアプリやデータも残ります。

リファレンス


Keelson Deploy Spec

Spec version: 2026-10-06 / Raw text (AI向け): /ja/docs/reference/deploy-spec.txt

この文書は、Keelson にアプリをデプロイする際の対応ランタイム・制約・成立条件を定義する正本です。デプロイの可否判断は、この文書に従ってください。

クイックスタートや操作手順は デプロイする を参照してください。


デプロイの完了は、ビルド結果だけでなく、アプリの種類に応じて確認してください。

アプリの種類完了時に確認する条件
Web アプリ・ハイブリッドビルドが完了し、Web アプリのプロセスが起動してヘルスチェックに通過している。アプリ URL が発行され、アクセスできる。ハイブリッドでは静的ファイルも配信できる
静的サイト・SPA配信するファイルのアップロード・公開が完了し、アプリ URL で配信できる。アプリのプロセス起動や Web のヘルスチェックは不要
定期実行専用アプリビルドが完了し、定期実行のスケジュールと実行環境の設定が反映されている。Web のプロセス起動・ヘルスチェック・URL アクセスは確認対象外

Web アプリでは、ビルドが成功しても、起動に失敗した場合やヘルスチェックに通らない場合はデプロイ成功ではありません。アプリ URL は https://<ワークスペース slug>--<アプリ slug>.keelson.run です。

定期実行ジョブはデプロイ後、設定したスケジュールに従って実行されます。デプロイの成功だけでは、個々のジョブの処理が成功したことにはなりません。実行結果はジョブの履歴・ログで確認してください。


すべてのデプロイには keelson.yaml が必要です。プロジェクトのルートディレクトリに配置します。

最小構成:

slug: my-app
runtime: python-slim
command: "python app.py"
db:
mode: none

db と db.mode(libsql または none)は必須です。

Flask などのフレームワークを使うアプリには本番用サーバーが必要です。本番環境向けの設定を参照してください。

各フィールドの詳細は keelson.yaml リファレンス を参照してください。


アプリは作成時に 1 つの論理リージョンへ配置されます。リージョンの論理キーは jp-tokyo(日本)と us-oregon(米国西海岸)です。現在新規作成に開放されているのは jp-tokyo のみで、us-oregon は定義済みですが未提供です。クラウド事業者のリージョン名ではなく、この表示名と論理キーを使います。

新規アプリでは keelson deploy --new --region <region>、または keelson.yaml のトップレベル region で配置先を指定できます。優先順位は CLI の指定、keelson.yaml、ワークスペースの既定リージョンの順です。

未提供のリージョン(現時点では us-oregon)を明示指定すると、別リージョンへ切り替えずに作成が拒否されます。指定がなければワークスペースの既定リージョン(日本)に配置されるため、ユーザーから求められない限り region は書かないでください。

リージョンはアプリ作成後に変更できません。既存アプリへ別のリージョンを指定しても移動は行われず、デプロイは拒否されます。別リージョンを使う場合は、新しいアプリとして作り直してください。


Keelson は、以下のランタイム上でのみアプリを実行できます。

ランタイム言語用途
python-slimPython軽量。API、テキスト処理、自動化など
python-mediaPythonメディア処理向け。画像・動画ライブラリを含む
node-slimNode.js軽量。Web アプリ、API など
node-mediaNode.jsメディア処理向け。画像処理ライブラリを含む
go-slimGo軽量
go-mediaGoメディア処理向け

keelson.yaml の runtime フィールドで指定します。迷った場合は -slim から始め、メディア処理系ライブラリが必要になったら -media に切り替えてください。

  • slim — 言語ランタイムと標準ライブラリのみ。ビルドが速く、イメージサイズが小さい
  • media — slim に加え、画像処理(Pillow、sharp 等)や動画処理に必要なシステムライブラリがプリインストールされている

特定のフレームワークに依存しません。command で起動でき、HTTP サーバーとしてリクエストを受け付けるアプリであれば動作します。

例: FastAPI、Flask、Express、Next.js、Hono、Gin など。


以下の言語・ランタイムはサポート対象外です。

  • Ruby
  • Java / Kotlin / Scala
  • PHP
  • Rust
  • .NET / C#
  • Elixir / Erlang
  • Swift

非対応ランタイムを必要とする構成のままでは、Keelson へデプロイできません。


Keelson は固定されたビルド環境・実行環境を提供します。アプリはその環境上でビルドおよび起動できる必要があります。

  • OS: Linux
  • CPU: x86_64 (amd64)

アプリは非 root ユーザーとして実行されます。sudo、apt-get install、システムレベルの変更は実行できません。

リポジトリにあっても無視されます(Dockerfile があること自体はデプロイを妨げません)。Keelson はランタイムを選択し、command で起動する方式です。Dockerfile の代わりに keelson.yaml でランタイムと起動コマンドを指定します。

パス書き込み永続化用途
/data不可(Keelson も作らず、アプリからも作れない)不可アプリは UID 1000 で実行され、root 所有の / 直下にはディレクトリを作れません
アプリディレクトリ(/workspace)直下への新規作成は不可。既存ファイルやサブディレクトリは権限によって書き込み可能不可ソースコード、依存関係
/tmp可(一時的)不可一時ファイル
ホームディレクトリ(/home/appuser)Python・Node.js ランタイムでは可不可ランタイムやツールのキャッシュ・設定
その他パスの所有者・権限による不可一時ファイルの保存には /tmp を使用
  • 一時ファイルの書き込み先には /tmp を使用してください。ローカルファイルへの書き込みは永続化されず、再起動やインスタンスの停止で失われます
  • Keelson が提供する永続ストレージは 3 種類です: Managed SQLite(db.mode: libsql)、Files SDK(アプリ内部の非公開ファイル)、Media SDK(利用者に配信する画像・添付)。いずれもローカルのファイルパスとしては利用できません。外部のデータベースやストレージへの接続も可能です
  • ファイル SQLite は永続化されません。ファイル SQLite クライアントの使用はデプロイ時に検出され、db.local_sqlite で一時用途と宣言しない限り拒否されます
  • Web アプリは環境変数 PORT で指定したポートで HTTP リクエストを待ち受けてください
  • 0.0.0.0 で listen する必要があります。127.0.0.1 や localhost ではリクエストが届きません
  • HTTPS 終端は Keelson が行います。アプリは HTTP で listen してください

Keelson は任意の OS パッケージ追加を前提とした環境ではありません。

  • -slim ランタイムには最小限のシステムライブラリのみ含まれます
  • -media ランタイムには画像・動画処理に必要な一般的なライブラリが含まれます
  • それ以外のシステムライブラリが必要な場合、アプリは動作しない可能性があります
  • apt-get 等によるパッケージ追加はできません(非 root のため)
  • command で起動した単一プロセスが基本です
  • systemd やデーモン管理は使えません
  • 時刻で動く処理には crons、リクエストの後に続く処理には tasks を使ってください(バックグラウンドタスク。workers は廃止済みで、宣言するとデプロイが拒否されます)

言語ランタイムが対応していても、依存ライブラリやシステム要件によってはデプロイできない場合があります。

言語パッケージマネージャで追加できる依存

Section titled “言語パッケージマネージャで追加できる依存”

以下のパッケージマネージャで管理される純粋な言語パッケージは問題なくインストールできます。

  • Python: pip(requirements.txt)
  • Node.js: npm(package.json)
  • Go: go mod(go.mod)

依存関係はイメージのビルド時に Keelson が自動でインストールします(requirements.txt / pyproject.toml / package.json / go.mod を検出)。command ではインストールせず、アプリの起動だけを行ってください。command に pip install / npm install / go build があると、CLI の事前チェックが command_installs_dependencies でデプロイを止めます。

ビルドの制約: Node のロックファイルは package-lock.json のみ。公開レジストリのみ(private registry・認証付きインストール不可)。Go は CGO_ENABLED=0。ビルド対象は linux/amd64。ビルド時にシークレットは渡らない。上限 600 秒。

# Python
command: "python app.py"
# Node.js
command: "npm start"
# Go (Keelson 側で deploy 時に `./app` がビルドされます)
command: "./app"

ネイティブ依存が必要なパッケージ

Section titled “ネイティブ依存が必要なパッケージ”

一部のパッケージは、C ライブラリやシステムレベルの依存を必要とします。

  • -media ランタイムで動作するもの: Pillow、opencv-python、sharp、ffmpeg 関連など、一般的なメディア処理ライブラリ
  • 動作しない可能性があるもの: ランタイムに含まれないシステムライブラリに依存するパッケージ

サポート対象外になる典型パターン

Section titled “サポート対象外になる典型パターン”
パターン理由
apt-get install が必要非 root でパッケージ追加不可
特殊な C ライブラリに依存ランタイムに含まれていない可能性
GPU を前提とした推論ライブラリGPU インスタンス未提供
データベースサーバー(PostgreSQL、MySQL、Redis)外部サービスとして接続は可能だが、Keelson 上での起動は不可
systemd やバックグラウンドデーモン前提プロセスモデルが異なる

デプロイモードは、command と assets の設定に応じて自動的に決まります。CLI や API に表示される deploy_mode の値とアプリの種類の対応は、次のとおりです。

アプリの種類deploy_mode の値commandassets説明
Web アプリcontainerありなし通常のアプリデプロイ
静的サイトedge-staticなしあり(fallback なし)静的ファイルのみ
SPAedge-spaなしあり(fallback あり)SPA(フォールバック付き)
ハイブリッドhybridありあり(type: web を明示した場合は fallback 必須)静的ファイル + バックエンド API
定期実行専用アプリcontainerなしなしcrons が必須。Web サーバーを起動せず、定期実行ジョブのみを実行

定期実行専用アプリも deploy_mode は container ですが、Web アプリとは起動・完了確認の条件が異なります。

静的サイト・SPA では type: web を指定します。crons は併用できません。


アプリのホスト上には、Keelson が内部処理に使うパスや、外部からのアクセスを拒否するパスがあります。次のパスをアプリの公開エンドポイントとして使わないでください。

  • /__keelson とその配下は、すべての HTTP メソッドでプラットフォーム内部用です。プラットフォームが配信するアセット・ファイルダウンロード・内部エンドポイントに使われます。これらのパスにアプリのルートを定義しないでください。
  • ビルド出力の配信ルート直下に __keelson ディレクトリを出力しないでください。 予約パスを含むビルドはビルド時にエラーコード reserved_path_conflict で拒否されます。ディレクトリ名を変更して再デプロイしてください。
  • GET /health と GET /_health は、ゲートウェイ経由ではプラットフォームが応答します。 アプリ側で同じパスを定義しても、その GET リクエストはアプリに届きません。
  • /api/webhooks/email と /api/webhooks/email-events は外部からアクセスできません。 エッジで拒否されるため、アプリ独自の Webhook には別のパスを使ってください。
  • /api/mcp と /api/mcp/ で始まるパスは、MCP のツール呼び出し専用です。 サーバー側のプログラムが動くアプリ(ハイブリッド構成のサーバー側を含む)では、keelson.yaml に mcp: を書いていなくても、MCP 経由以外のリクエストは 404 になります。このパスにはツールのハンドラ(POST /api/mcp/<ツール名>)以外のルートを置かないでください。静的に配信される部分には影響しません。
  • mcp.enabled: true のアプリでは、/mcp、/.well-known/oauth-protected-resource、/.well-known/oauth-protected-resource/mcp は Keelson が応答します。 これらのリクエストはアプリに届きません。詳しくは AI クライアントから使う(MCP)を参照してください。
  • 関連ルール: auth.endpoints のパスは /__keelson で始められません(/api/external/ または /api/webhooks/ で始める必要があります)。また slug に予約語は使えません。

/assets/*(Vite の既定出力先)、/files/*、/static/*、/uploads/* などはアプリで利用できます。/api/* も、上記の例外と認証設定のルールに従って利用できます。

フレームワーク静的パス衝突マトリクス(参考)

Section titled “フレームワーク静的パス衝突マトリクス(参考)”

主要フレームワークが既定で出力する静的パスの参考一覧です。「確認済み」は本リポジトリで配信を確認したもの、「未検証」は既定パスからの推定です。未検証のフレームワークでは、実際のビルド出力と配信を確認してください。この表は /__keelson とのパス衝突の参考情報であり、フレームワークの対応可否を示すものではありません。

フレームワーク既定の静的パス/__keelson と衝突状態
Vite(Vue / Svelte / React / Solid / Preact)/assets/*しない確認済み
Remix v2 / VitePress/assets/*しない見込み未検証
Angular/assets/*しない見込み未検証
Next.js/_next/static/*しない見込み未検証
Nuxt / SvelteKit / Astro/_nuxt/* · /_app/* · /_astro/*しない見込み未検証
CRA / Django / Flask/static/*しない見込み未検証

アプリの起動に必要な API キー、トークン、接続情報は、コードに直接埋め込まず、環境変数またはシークレットとして設定してください。

  • keelson.yaml の env — バージョン管理に含めてよい値
  • コンソールのシークレット — API キーやトークンなど、コードに含めたくない値

必要な値が未設定の場合、アプリは正常に起動できないことがあります。

詳細は 環境変数とシークレット を参照してください。

変数名説明
PORTアプリが listen するポート。読み取り専用で、自分で設定しない
TZワークスペースのタイムゾーン。ワークスペース作成時にブラウザから自動決定される
KEELSON_MODEKeelson 上で動作していることを示す印(値は keelson)
KEELSON_APP_IDアプリの内部 ID
KEELSON_WORKSPACE_IDワークスペースの内部 ID
KEELSON_TENANT_IDKEELSON_WORKSPACE_ID の互換用別名(値は同じ)
KEELSON_DEPLOY_ID現在のデプロイの内部 ID

旧 tenant 名の変数も互換のため受け付けます。撤去時期は未定です。

db.mode: libsql のときは KEELSON_DB_URL / KEELSON_DB_AUTH_TOKEN(別名 TURSO_DATABASE_URL / TURSO_AUTH_TOKEN)が追加で注入されます。KEELSON_ で始まる名前はプラットフォーム予約で、env や secrets には使えません。


Keelson のアプリは Cloud Run 上で動作し、通常運用中にも起動と停止が発生します。停止時にはプロセスへ SIGTERM が送られ、10 秒後に SIGKILL が送られます。本番用サーバーを使い、この時間内にドレイン処理を完了してください。

フレームワーク別の本番サーバ

Section titled “フレームワーク別の本番サーバ”

Flask: app.run() は Werkzeug 開発サーバーを起動します。ローカル用の __main__ エントリーポイントの中に残すことはできますが、debug=False でもデプロイ先のサーバーにしてはいけません。gunicorn を requirements.txt に追加し、次のコマンドを使います。

command: "gunicorn --bind 0.0.0.0:$PORT --workers 1 --threads 8 --timeout 0 --graceful-timeout 9 app:app"

Keelson は 1 vCPU のため、worker は 1 にします。graceful timeout を 9 秒にすると、10 秒の SIGTERM 猶予内に収まります。app:app はプロジェクトのモジュール名とアプリケーションオブジェクト名に置き換えてください。

Django: gunicorn で Django を起動します。

command: "gunicorn --bind 0.0.0.0:$PORT --workers 1 --threads 8 --timeout 0 --graceful-timeout 9 config.wsgi:application"
  • DEBUG は既定で無効にします。
  • DEBUG=False の場合、Django 自身は静的ファイルを配信しません。whitenoise を MIDDLEWARE に追加し、STATIC_ROOT を設定して、collectstatic 済みの成果物を配信対象に含めてください。
  • ALLOWED_HOSTS = ["*"] は維持してください。制限するとヘルスチェックが HTTP 400 になることがあります。
  • config.wsgi:application はプロジェクトの WSGI モジュールに置き換えてください。

FastAPI / uvicorn: 0.0.0.0:$PORT にバインドし、worker 数は既定の 1 のままにします。

command: "uvicorn main:app --host 0.0.0.0 --port $PORT"

--reload は使わないでください。ファイル監視がメモリを消費し、アプリが二重起動することがあります。main:app はプロジェクトのモジュール名とアプリケーションオブジェクト名に置き換えてください。

Next.js: 開発サーバーではなく、ビルド済みのアプリを起動します。

command: "npm run start"

package script は次のように設定します。

"start": "next start -p $PORT"

package.json に "start": "next dev" がある場合、回避策を加えるのではなく、その script を修正してください。next dev は本番用サーバーではありません。

Node.js: Keelson は NODE_ENV を自動設定しません。keelson.yaml で本番モードを宣言してください。未設定の場合、Express などのフレームワークがスタックトレースを返すことがあります。

env:
NODE_ENV: "production"

Go: ビルド済みバイナリを command: "./app" で起動します。ListenAndServe を停止処理なしで放置せず、signal.NotifyContext と server.Shutdown を使って新規リクエストの受付を止め、処理中のリクエストを 10 秒の猶予内に完了させてください。

既定値は本番で安全な向きにします。Python の DEBUG は false を既定にします。

DEBUG = os.environ.get("DEBUG", "false").lower() == "true"

true を既定にしてはいけません。

DEBUG = os.environ.get("DEBUG", "true").lower() == "true"

デバッグ画面は、データベースの認証トークンを含む環境変数を表示することがあります。

Keelson が値 keelson で設定する KEELSON_MODE を使って、プラットフォーム上での実行を判別します。

ON_KEELSON = os.environ.get("KEELSON_MODE") == "keelson"

本番設定の不足を即時エラーにする検査は、ON_KEELSON が true の場合だけ適用してください。DEBUG をプラットフォーム判別に使うとローカルで起動できなくなるため、代用しないでください。


対象制限
HTTP リクエスト120 秒以内にレスポンスを開始(超過で 504)。ストリーミングは開始後、無通信 120 秒・合計 300 秒まで
定期実行ジョブ(cron)1〜600 秒(既定 300 秒。プラン上限がそれより低ければ切り詰め)
ビルド600 秒

  • アプリから外部 API やサービスへの通信はデフォルトで可能です
  • 例外は SMTP のポート(TCP 25・465・587・2525)で、常に遮断されます。メール送信はメール配信サービスの HTTPS API を使ってください

症状原因対処
起動しているが接続できない、ヘルスチェックに失敗する0.0.0.0 で listen していないhost="0.0.0.0" を明示
ポートに接続できないポート番号がハードコード環境変数 PORT から読み取る
モジュールが見つからない依存マニフェストに記載がない、またはイメージのビルド時にインストールが失敗ビルドログを確認し、依存を requirements.txt または package.json に記載する
ネイティブモジュールのビルド失敗システムライブラリ不足-media ランタイムに変更、または依存を見直す
起動コマンドが見つからないエントリポイントのパスが間違いファイル名・パスを確認
事前チェックが command_installs_dependencies で止まるcommand に pip install / npm install がある削除して起動コマンドだけにする
検証が db 不足で止まるdb.mode が無いlibsql または none を宣言する
ビルドが reserved_path_conflict で失敗ビルド出力の配信ルート直下に __keelson(予約 URL プレフィックス)があるディレクトリ名を変更する。ほかの制限対象パスは予約 URL パスを参照
定期ジョブが一度も発火しない(エラーも出ない)スケジュールがアプリのプロセス内にある(in-process スケジューラ)。予定時刻にはアプリがゼロにスケールしているcrons エントリとして宣言する
UI は成功と表示されたのにメール・通知が届かないレスポンスを返した後に回した処理が走っていないレスポンスを返す前に済ませるか、バックグラウンドタスクに投入する。タスクを使わない場合は、処理内容を DB に保存し、crons で定期的に読み出して実行する。次の定期実行まで待機し、実行状況によってはさらに遅れることをユーザーに伝える
  1. ビルドログを確認 — 依存関係のインストールが成功しているか
  2. 実行ログを確認 — 起動時エラーやランタイムエラーがないか
  3. keelson.yaml を確認 — runtime、command、env の設定が正しいか

AI エージェントは、Keelson へのデプロイ時に以下の順序で判断してください。

  1. ランタイム確認 — アプリの言語がこの仕様の対応ランタイムに含まれるか確認する
  2. 依存関係確認 — ネイティブ依存やシステムライブラリの要件が制約内か確認する
  3. keelson.yaml 確認 — ファイルの有無と設定内容の妥当性を確認する。なければ作成する
  4. 環境変数確認 — 必要な環境変数やシークレットが不足していないか確認する
  5. デプロイ実行 — CLI または Skill を使ってデプロイを実行する
  6. 完了確認 — デプロイ成功の定義に従い、アプリの種類に応じて確認する。Web アプリは起動・ヘルスチェック・URL アクセス、静的サイト・SPA は URL での配信、定期実行専用アプリは設定の反映を確認する
  7. 失敗時対応 — ログを確認し、修正可能な範囲で対処する。制約違反がある場合は理由を明示して停止する
  • 非対応ランタイムの場合は、デプロイを試行しない。理由を明示して停止する
  • 必須設定が不足している場合は、必要事項を提示する
  • ビルド成功のみで完了扱いにしない
  • 起動失敗時は必ずログを参照する
  1. Web 上の最新 Keelson Deploy Spec(この文書)
  2. Web 上の最新 CLI バージョン情報
  3. Skill 同梱版の仕様
  4. 一般知識・推測

Web 上の正本と Skill 同梱の情報が矛盾する場合は、Web 上の正本を優先してください。


keelson.yaml

keelson.yaml はプロジェクトルートに置くデプロイ設定ファイルです。ランタイム、起動コマンド、環境変数、データベース、定期実行ジョブ、静的アセットなどを定義します。

デプロイ時に Keelson はこのファイルを読み、ビルドと実行環境を決めます。入門は keelson.yaml の設定を参照してください。

slug: my-app
runtime: python-slim
command: "python app.py"
db:
mode: none

フィールド型必須既定値説明
slugstringはい—アプリの識別子。アプリ作成時に使われる
workspacestring—nullプロジェクト単位の CLI 操作で使う既定ワークスペース。ワークスペースの slug を推奨。所属が 1 つだけなら CLI が自動選択するため書かなくてよい。明示した --workspace はこの値より優先し、apps list の一覧は絞り込まない
descriptionstring—nullアプリの説明(300 文字以内)
iconstring—nullコンソールでアプリのアイコンとして表示する画像。keelson.yaml のあるフォルダからの相対パス。icon を参照
typestring条件付きnullアプリ種別。web のみ。起動コマンドのない静的サイト・SPA では必須
runtimestringはい—実行環境。対応ランタイムを参照
commandstring | list条件付き—起動コマンド。定期実行ジョブのみ、または type: web と assets を指定した静的サイトでは省略可
envmap—{}秘密でない環境変数
dbobjectはい—データベースとローカル SQLite の方針
cronslist条件付き[]定期実行ジョブ。command がなく、静的サイト(type: web と assets)でもない場合は必須
taskslist—[]アプリが SDK で投入するバックグラウンドタスク。tasks を参照
mcpobject—nullAI クライアントから MCP で使うツールの宣言。mcp を参照
assetsobject—null静的アセット配信
healthobject—nullデプロイ時ヘルスチェックのパス
secretsobject—{}シークレットの宣言と必須条件
authobject—null対話ログインを通さない外部向けエンドポイント
verifystring | list—[]デプロイ後に追加で検証するパス
regionstring—null新規アプリの配置リージョン
storageobject—{}廃止された disk_id だけを受理する互換ブロック

廃止されたトップレベルキー databases と workers は、値が null や空リストでも拒否されます。継続して保存する業務データは db.mode: libsql、リクエストの後に実行する処理は tasks、決まった時刻に実行する処理は crons で表現してください。

旧名のトップレベル tenant も workspace の互換用別名として受け付けます。撤去時期は 未定です。両方を書く場合、前後の空白や大文字小文字も含めて、値が完全に一致している必要があります。

上の表にないトップレベルキー(build、port、scaling など)や、各オブジェクトの中の未知のキーは拒否されます。独自のキーを足せる拡張可能な形式ではありません。crons / secrets / storage に null を書くと省略と同じにはならず拒否されます。省略したいときはキーごと書かないでください。

迷ったら、文字列の値は引用符で囲んでください。 CLI と API で同じ文字列として読み取れるようにするためです。

次のような文字列は、引用符で囲むか、ブロックスカラー(| / >)で書く必要があります。引用符なしで書くと、string_value_not_portable で拒否されます。

  • 数字で始まる値(slug: "123-app")
  • 日本語などの非 ASCII 文字で始まる値(description: "営業向けアプリ")
  • - で始まる値。コマンドの引数も対象です(command: [python, app.py, "--port", "8080"])
  • YAML が真偽値や null と解釈する語(yes / no / on / off / true / false / null と、env のキーの制約に列挙した大文字小文字の表記)
  • : で始まる値(":memory:")など、下記の例外に当てはまらない記号で始まる値

引用符を省略できるのは、次の場合です。ただし、上に挙げた真偽値・null と解釈される語は除きます。

値の種類引用符を省略できる書き方例
slug、runtime、シークレット名などの識別子半角英字か _ で始まるmy-app、python-slim、OPENAI_API_KEY
コマンド、説明文、パスなどの文字列半角英字か _、または /、./、../ で始まるpython app.py、/health、./app

: を含むだけで引用符が必須になるわけではありません。たとえば description: https://example.com は引用符なしで書けます。ただし、: (コロンと空白)を含む文字列など、YAML 自体の構文上、引用符が必要な場合もあります。引用符を付けても、各フィールドのパス形式や文字数などの制約は変わりません。

env の値は例外で、先頭の文字にかかわらず、すべて引用符かブロックスカラーで書く必要があります(env を参照)。


アプリの識別子です。アプリ作成時の公開 URL(https://<ワークスペース slug>--<slug>.keelson.run)の一部になります。

slug: my-app

ルール:

  • 小文字英数字とハイフン(a-z、0-9、-)のみ
  • 1〜63 文字
  • 先頭と末尾は英数字
  • ハイフンの連続(--)は不可
  • 予約語: admin api assets auth console health static www

public_slug は廃止された互換フィールドで、無視されます。


アプリが誰向けで何をするかを示す 1〜2 文です。

description: "営業チーム向けの日報・週報作成アプリ。週次サマリーを自動集計します"

前後の空白を除いて 300 文字(Unicode コードポイント)以内。超えるとデプロイが失敗します。コンソールのアプリ説明欄に表示され、説明がまだ空のときだけ適用されます。コンソールで編集した説明がデプロイで上書きされることはありません。


コンソールのアプリ一覧・アプリ詳細でアプリのアイコンとして表示する画像です。省略できます。指定しないアプリは名前の頭文字で表示され、テンプレートから作ったアプリはテンプレートのアイコンで表示されます。

icon: ./assets/app-icon.png
  • パスは keelson.yaml のあるフォルダからの相対パスです。コマンドを実行したフォルダは関係ありません。プロジェクトの外を指すパス(絶対パス、.. で外に出るパス)、\ を含むパス、シンボリックリンクは使えません。
  • 画像はソースと一緒にアップロードされるため、アーカイブから除外される場所には置けません(アーカイブに含まれるものを参照)。command のない静的サイトでは assets.dir の中に置いてください。
  • 使える画像は PNG・JPEG・WebP で、256KB 以下、縦横とも 32〜2048 ピクセルです。形式は拡張子ではなくファイルの中身で判定します。SVG は使えません。
  • keelson deploy(--check を含む)は、アプリを作成・アップロードする前に画像を確かめ、問題があればパスと原因を表示して止まります。原因は icon.path(見つからない・読めない・シンボリックリンク・アップロードされない)、icon.format、icon.size、icon.dimensions、icon.corrupt(画像のヘッダーが読めない)のいずれかです。

アイコンはデプロイが成功した時点で反映され、CLI は完了時に Icon: の行を 1 行表示します(--ndjson では icon_outcome)。

icon_outcome意味
appliedkeelson.yaml の画像をアプリのアイコンにした
unchangedすでに同じ画像がアイコンになっている
kept_console_iconコンソールでアップロードしたアイコンを残した(keelson.yaml では置き換えない)
kept_clearedコンソールでアイコンが削除されているため、デプロイでは戻さない

コンソールで設定・削除したアイコンは、常に keelson.yaml より優先されます。その後で keelson.yaml の画像を使いたい場合は、コンソールのアプリの「設定」でその画像をアップロードしてください。keelson.yaml から icon を消しても、アプリのアイコンは消えません。

アプリアイコンは順次提供中です。デプロイが icon is not available yet で拒否された場合は、keelson.yaml から icon を消して再度デプロイしてください。


アプリ種別です。起動コマンドのない静的サイト・SPA では type: web が必須です。それ以外では省略できます。

type: web

指定できる値は web だけです。type: web を指定すると、assets だけで command なしのデプロイができます。

制約:

  • type: web と crons は併用できません
  • Web アプリと定期実行ジョブを両方持つアプリでは type を省略します

実行環境です。静的サイトだけのデプロイでも必須ですが、静的サイトではビルドに使われません。静的サイトは手元や CI でビルド済みの assets.dir をそのまま配信します。

ランタイム言語用途
python-slimPython軽量。API、テキスト処理など
python-mediaPython画像・動画処理(Pillow、opencv、ffmpeg 関連など)
node-slimNode.js軽量
node-mediaNode.js画像処理(sharp など)
go-slimGo軽量
go-mediaGoメディア処理

迷ったら -slim から始め、メディア処理系ライブラリが必要になったら -media に切り替えます。

コンテナ型とハイブリッド型では、依存パッケージはビルド時に Keelson が自動でインストールします。静的サイト(type: web と assets を指定し、command なし)ではこの処理は行われません。既定でアップロードされるのは assets.dir 配下のファイルと keelson.yaml だけです(アーカイブに含まれるもの)。command には起動コマンドだけを書いてください。command に pip install / npm install / go build を含めると、CLI の事前チェックが command_installs_dependencies でデプロイを止めます。

ランタイム検出するファイルビルド時に実行される処理
python-*requirements.txtpython -m pip install --user -r requirements.txt
python-*pyproject.toml([project] または [build-system] あり)。requirements.txt がない場合のみpython -m pip install --user .
node-*package-lock.json / package.jsonnpm ci(ロックファイルあり)または npm install、続けて npm run build --if-present
go-*go.mod / go.sumgo mod download、go build -o /workspace/app .(command: "./app" で起動)

ビルドの制約:

  • requirements.txt と pyproject.toml が両方あるときは requirements.txt だけを使います。pyproject.toml 側だけに書いた依存や自作パッケージのインストールは行われないので、必要なものは requirements.txt に含めてください
  • インストール可能な Python の pyproject.toml には、対応するロックファイル(requirements.txt / poetry.lock / uv.lock / Pipfile.lock)のいずれかが必要です
  • Node.js のビルドは npm のみを使います。外部パッケージへの依存を宣言するアプリには package-lock.json が必要です。開発用・任意・peer 依存も対象です。外部依存の宣言がなければ、ロックファイルは省略できます。ロックファイルがなく、package.json がある場合は npm install が実行されます。pnpm-lock.yaml / yarn.lock だけの場合は事前チェックが lockfile_unsupported でデプロイを止めます。package-lock.json と併存している場合はそのまま残してかまいません
  • サードパーティ依存を宣言する Go モジュールには go.sum が必要です。標準ライブラリだけを使うモジュールには必要ありません
  • 公開レジストリからのみ取得できます。private registry、git+ssh 依存、認証が必要なインストールは失敗します
  • Go は CGO_ENABLED=0 でビルドされます
  • ビルド対象は linux/amd64 のみ
  • ビルド時にシークレットは渡りません
  • ビルドの上限時間は 600 秒

アプリ起動時に実行するコマンドです。文字列(シェル経由)またはリスト(直接実行)で指定します。

# 文字列形式(/bin/sh -c で実行)
command: "python app.py"
# リスト形式(exec で直接実行)
command:
- python
- app.py

ルール:

  • 起動コマンド(command)、定期実行ジョブ(crons)、静的サイト(type: web と assets)のいずれかが必要です
  • 定期実行ジョブだけのアプリに command は不要です
  • type: web で assets がある静的サイトに command は不要です
  • 空文字列・空リストは「起動コマンドなし」として扱われます。通常の Web アプリでは指定が必要です。crons[].command は空にできません
  • 依存関係のインストールは含めません(依存関係のインストールを参照)

秘密でない環境変数をキーと値で定義します。値はすべて文字列です。

PORT は Keelson が実行時に設定します。env に書いても無視されます。

値は必ず引用符で囲むか、ブロックスカラー(| / >)で書いてください。 引用符のない値は env_value_not_string で拒否され、デプロイは開始されません。数値・真偽値も同様です。

env:
NODE_ENV: "production"
DEBUG: "false"
LOG_LEVEL: "info"

引用符が必須なのは、keelson.yaml を CLI(YAML 1.2)と API(YAML 1.1)の 2 つの実装が解析しており、引用符のないスカラーの型解決が食い違うためです。たとえば K: 0o123 は経路によって "83" にも "0o123" にもなります。引用符で囲めば両方で同じ文字列になります。

引用符を付けないキーは、英字か _ で始まり、英数字と _ だけで構成される必要があります。NODE_ENV や DB_POOL のような通常の環境変数名はそのまま書けます。

次の語は YAML 1.1 で真偽値・null と解釈されるため、引用符なしでは使えません:

yes Yes YES no No NO true True TRUE false False FALSE on On ON off Off OFF null Null NULL

ハイフンを含む名前や予約語は、キーを引用符で囲みます。

env:
NODE_ENV: "production"
"MY-VAR": "x"
"yes": "x"

KEELSON_ で始まるキー(大文字小文字を区別しない)はプラットフォーム予約で、env には設定できません。env の重複宣言、env 内のキー重複、YAML の merge key(<<)も拒否されます。

秘密の値は env ではなく secrets で宣言し、値はコンソールまたは CLI で設定します。env と同じ名前のシークレットが設定されている場合はシークレットの値が優先され、env の変更は反映されません。


データベースの扱いを選びます。db と db.mode は必須です。

db:
mode: libsql
フィールド型必須既定値説明
modestringはい—libsql または none。旧名 turso は libsql に正規化
migratestring—nullデプロイ時に実行するマイグレーションコマンド。libsql のときのみ有効
auto_adoptboolean—false旧設定との互換のため受理されるが、現在は何もしない。DB を引き継ぐ機能ではない
local_sqliteobject—null再生成可能な一時 SQLite ファイルの明示宣言
mode意味
libsqlKeelson がアプリ専用の Managed SQLite を用意し、KEELSON_DB_URL / KEELSON_DB_AUTH_TOKEN(別名 TURSO_DATABASE_URL / TURSO_AUTH_TOKEN)を注入する
noneKeelson は DB を管理しない。DB 不要のアプリ、または外部 DB を使うアプリ

mode: libsql は契約も接続設定も不要で、scale-to-zero に対応します。継続して保存する業務データにはこれを推奨します。

PostgreSQL、MySQL、自前の libSQL を使う場合は mode: none にし、接続情報をシークレットで渡します。外部 DB の接続情報を Keelson が注入することはありません。

ファイル SQLite を永続化するモードはありません(/data は存在せず、アプリからも作成できません)。ファイル SQLite を使うコード(sqlite3、better-sqlite3 など)はデプロイ時に検出され、local_sqlite の宣言がなければ拒否されます。

db.migrate は、新しいイメージでトラフィックを切り替える前に 1 回だけ実行されるコマンドです。

db:
mode: libsql
migrate: "python migrate.py"
  • mode: libsql のときのみ有効。文字列で指定します
  • コンテナ型・ハイブリッド型でのみ実行されます。静的サイトにはコンテナがないため、宣言しても実行されません
  • 候補リビジョンがヘルスチェックを通った後、トラフィック切り替えの前に、デプロイ対象のイメージ上で実行されます。アプリの起動コードは移行前のスキーマで動く必要があります
  • 終了コードが 0 以外ならデプロイは deploy.runtime.migration_failed で失敗し、旧リビジョンが維持されます
  • 毎回のデプロイで実行されるため、冪等に書きます(CREATE TABLE IF NOT EXISTS、ON CONFLICT DO NOTHING)

再生成できるキャッシュ用途のファイルだけを db.local_sqlite で宣言します。宣言してもファイルは永続化されません。

db:
mode: none
local_sqlite:
policy: ephemeral
paths:
- /tmp/cache.db
reason: "外部 API から再構築できる派生キャッシュ"
フィールド型必須制約
policystringはいephemeral のみ
pathslistはい空でないこと。各パスは /tmp/ 配下か :memory:
reasonstringはい消えても問題ない理由

廃止された databases ブロックは常に拒否されます。


定期実行ジョブを定義します。各ジョブは Web サービスとは別のインスタンスで実行されます。

crons:
- name: cleanup
description: "期限切れのセッションを削除する"
schedule: "0 3 * * *"
command: "python cleanup.py"
timeout: 60
enabled: true
フィールド型必須既定値説明
namestringはい—ジョブ名。小文字英数字とハイフン、1〜63 文字、重複不可
schedulestringはい—5 フィールドの cron 式
commandstring | listはい—実行コマンド
timeoutinteger—300タイムアウト秒。1〜600
enabledboolean—truefalse にするとスケジュールされない
descriptionstring——ジョブの 1 行説明。コンソールの定期実行タブに表示する。下の crons[].description を参照

ルール:

  • 1 アプリ最大 10 本。プランによりさらに少ない上限があります
  • crons[].description は表示専用です。前後の空白を除き、空なら未指定と同じです。除いた後で 100 文字を超えると cron_description_too_long で拒否されます(切り詰めはしません)。書き換えても実行内容は変わりません
  • type: web と併用できません
  • 別インスタンスで実行されるため、ローカルファイル(/tmp を含む)は Web と共有されません。共有する状態は db.mode: libsql に置きます
  • 前回の実行が継続中なら、その回はスキップされます
  • スケジュールはワークスペースのタイムゾーンで評価されます
  • timeout の絶対上限は 600 秒。省略時は 300 秒ですが、プランの上限がそれより低ければプラン上限に切り詰められます

本数・最小間隔・timeout 上限のプラン別の値はプランと制限を参照してください。

式意味
0 * * * *毎時 0 分
*/15 * * * *15 分ごと
0 3 * * *毎日 3:00
0 9 * * 1-5平日 9:00
0 0 1 * *毎月 1 日 0:00

* * * * *(毎分)は全プランの最小間隔より短いため拒否されます。実行の挙動と設計は定期実行ジョブを参照してください。


バックグラウンドタスクを宣言します。アプリのコードが SDK で名前を指定して投入すると、Keelson は宣言したコマンドを Web サービスとは別のインスタンスで 1 回起動し、失敗した試行は自動で再試行します。crons と違い、時刻では起動しません。投入の仕方・コマンドの書き方・2 回走っても壊れない書き方はバックグラウンドタスクを参照してください。

command: "python app.py"
tasks:
- name: generate-pdf
description: "注文の請求書 PDF を作る"
command: "python generate_pdf.py"
timeout: 180
max_attempts: 5
フィールド型必須既定値説明
namestringはい—タスク名。小文字英数字とハイフン、1〜63 文字(crons[].name と同じ規則)。前後の空白を除いて小文字化して扱うため、Foo は foo として受け付ける
commandstring | listはい—実行コマンド。文字列は /bin/sh -c、リストは直接実行
timeoutinteger—3001 回の試行の制限時間(秒)。1〜600
max_attemptsinteger—3初回を含む試行の最大回数。1〜5
descriptionstring——タスクの 1 行説明。コンソールのバックグラウンドタスクタブに表示する。下の tasks[].description を参照

ルール:

  • 1 アプリ最大 10 本(全プラン共通)
  • タスク名は重複できず、crons のジョブと同じ名前も使えません(task_name_conflicts_with_cron)。名前は前後の空白を除いて小文字化した後で比べます
  • tasks があるのに command も crons も無い設定は、タスクを投入するコードが無いため tasks_require_command_or_crons で拒否されます。type: web と assets だけで command の無い静的サイトもこれに当たります
  • type: web と併用できます
  • tasks[].description は表示専用です。前後の空白を除き、空なら未指定と同じです。除いた後で 100 文字を超えると task_description_too_long で拒否されます(切り詰めはしません)。書き換えても実行内容は変わりません
  • 未知のキーは拒否されます。同時実行数は宣言できません(プランの値を Keelson がかけます)
  • timeout の絶対上限は 600 秒。明示した値がプランの上限を超えると plan_task_timeout で拒否されます。省略時の 300 秒がプランの上限より大きければ、プランの上限に切り下げます
  • 各試行の標準入力に、attempt_no・payload・schema_version・task_id・task_name を持つ JSON が 1 行で渡されます。終了コード 0 が成功です。別インスタンスで実行されるため、ローカルファイルは Web と共有されません

timeout 上限のプラン別の値はプランと制限を参照してください。


アプリのツールを宣言し、Claude や ChatGPT などの AI クライアントから MCP で使えるようにします。ツールが呼ばれると、Keelson がアプリの POST /api/mcp/<name> を呼びます。ログイン・同意画面・AI クライアントとのやり取りは Keelson が受け持ちます。ハンドラの書き方・接続・上限は AI クライアントから使う(MCP)を参照してください。

command: "python app.py"
mcp:
enabled: true
instructions: "Look up stock levels and record incoming deliveries."
tools:
- name: search_items
description: "Search stock by item name."
access: read
input:
type: object
properties:
query:
type: string
required: [query]
- name: create_arrival
description: "Record one incoming delivery."
access: write
permission: manage
input:
type: object
properties:
item_id:
type: string
quantity:
type: integer
minimum: 1
required: [item_id, quantity]
フィールド型必須既定値説明
enabledbooleanはい—true でツールを公開する。false は宣言を残したまま呼び出しを止める(既存の接続は失効しない)
instructionsstring—nullAI クライアントに渡す、ツール全体の説明。4096 文字以内
toolslist条件付き—ツールの一覧。enabled: true なら必須。書いた場合は enabled にかかわらず 1〜40 本
filestring——instructions と tools を置く別ファイル。mcp.file を参照。新しい Keelson CLI が必要
tools[].namestringはい—ツール名。^[a-z][a-z0-9_]{0,63}$(英小文字で始まり、英小文字・数字・_ だけ。小文字化などの正規化はしない)。重複不可。ハンドラのパスは POST /api/mcp/<name>
tools[].descriptionstringはい—AI クライアントに見せるツールの説明。1〜1024 文字。空白だけは不可
tools[].accessstringはい—read か write。write は同意画面で別に表示され、利用者は読み取りだけを承認できる
tools[].permissionstring—viewview か manage。manage のツールはアプリの管理権限を持つ人だけが使える
tools[].inputobjectはい—引数の JSON Schema(2020-12)。最上位は type: object

ルール:

  • 宣言できるのは、サーバー側のプログラムが動くアプリ(command があり assets がない)だけです。静的サイト・SPA・ハイブリッドでは mcp is only supported for container apps で拒否されます
  • mcp と tools[] の未知のキーは拒否されます。ハンドラのパスは固定なので、tools[].endpoint は書けません
  • input は JSON として表せる値だけで書きます。入れ子は 32 段まで、JSON にして 64 KB まで。日付や、引用符なしでは文字列以外に読まれるキー(yes・on・数値など)は引用符で囲みます
  • input の中で $ref・$dynamicRef・$recursiveRef・x-mcp-header は使わないでください。$ref と x-mcp-header はデプロイで拒否され、$dynamicRef と $recursiveRef はデプロイを通っても呼び出しがすべて失敗します
  • mcp の中では YAML のアンカー・エイリアス・マージキー(<<)は使えません
  • mcp: を書いていなくても、サーバー側のプログラムが動くアプリでは /api/mcp と /api/mcp/ で始まるパスは MCP 専用です(予約 URL パス)

ツールが多いときは、instructions と tools を別の YAML ファイルに置けます。keelson.yaml には enabled と file だけを書きます。

command: "python app.py"
mcp:
enabled: true
file: mcp.yaml
# mcp.yaml(keelson.yaml と同じフォルダ)
instructions: "Look up stock levels and record incoming deliveries."
tools:
- name: search_items
description: "Search stock by item name."
access: read
input:
type: object
properties:
query:
type: string
required: [query]
  • デプロイ時に Keelson CLI が別ファイルの中身を keelson.yaml の mcp に差し込んでから検査・送信します。手元の keelson.yaml は書き換えません
  • 別ファイルのトップレベルに書けるのは instructions と tools だけです(どちらも省略可)。mcp.file と mcp.instructions / mcp.tools は同時に書けません
  • パスは keelson.yaml のあるフォルダからの相対パスで、そのフォルダの外(.. やシンボリックリンクで外に出る場合を含む)は指せません。拡張子は .yaml か .yml、大きさは 4 MiB まで
  • 別ファイルの中の誤りは、mcp.yaml: tools[3].input ... のように別ファイル名と位置で示されます
  • mcp.file を読めるのは新しい Keelson CLI だけです。古い CLI は mcp contains unsupported field(s): file でデプロイを拒否します。keelson upgrade で CLI を最新版にしてください。CLI を通さない経路では使えません

workers(バックグラウンドワーカー)は廃止されました。トップレベルに workers があると、値が空リストや null でも workers_not_supported で拒否されます。

リクエストを受けて走らせる処理は tasks に宣言し、SDK で投入します(バックグラウンドタスク)。未処理データを定期的に処理する場合は、crons に定期実行ジョブを定義します。プランで許可される間隔で実行し、1 回ごとに未処理分を処理して終了する形にします。

# NG — デプロイが拒否される
workers:
- name: drain
command: "python worker.py"
every: 10m
# OK — crons で書く
crons:
- name: drain
schedule: "*/5 * * * *"
command: "python drain.py"
timeout: 120

静的サイト、SPA、静的ファイル + バックエンド API(ハイブリッド)の配信設定です。

assets:
dir: dist
fallback: index.html
api: /api
フィールド型必須既定値説明
dirstringはい—アセットディレクトリ(プロジェクトルートからの相対パス)
static_dirstring—nulldir の廃止された別名。両方ある場合は一致が必要
fallbackstring条件付きnullSPA 用フォールバック(例: index.html)。dir 配下の相対パス。type: web を指定したハイブリッドでは必須
apistring—null(ハイブリッドでは実効値 /api)バックエンドへ転送するパスプレフィックス。command と assets の両方があるハイブリッドで省略すると /api が使われる

ハイブリッドでは api のパス配下がバックエンドへ転送され、それ以外は静的アセットとして配信されます。省略時のプレフィックスは /api です。別のパスにしたいときだけ指定します。

ルール:

  • dir に .. は使えません
  • api は / で始まり、/ そのものではなく、/__keelson 配下でないこと
  • api はトップレベルの command がある場合のみ有効
  • type: web を指定し、assets と command の両方があるハイブリッドでは fallback が必須です。type を省略した場合、設定検証では fallback の省略が認められます。SPA の画面を配信する場合は index.html などを指定してください

静的サイト(type: web と assets を指定し、command なし)では、既定で assets.dir 配下のファイルと keelson.yaml だけをアップロードします。コンテナ型・ハイブリッド型では、ビルドに必要なソースも含めます。どちらも、以下の除外ルールが適用されます。

assets.dir: . や同じ意味の ./ は避けてください。プロジェクト直下が公開対象となり、アーカイブに含まれる全ファイルが配信される可能性があります。CLI はデプロイ前に警告しますが、この設定は禁止しません。

  • パスの構成要素が .git、.venv、__pycache__、.pytest_cache、node_modules、dist、build、.idea、.vscode、.DS_Store に一致するファイルやディレクトリ
  • ファイル名が .env で始まるか終わるファイル(.env、.env.production、.envrc、.secrets.env など)。assets.dir の中でも除外されます
  • シンボリックリンク、通常ファイル以外、--secrets-from-env-file で指定したファイル

宣言した assets.dir と、そこに至るまでのディレクトリは、名前による除外の対象外です。そのため、assets.dir: dist なら dist のビルド出力を含められます。ただし、その中の dist/node_modules/** などは除外されます。.git はこの例外の対象になりません。

プロジェクト固有の除外は、プロジェクト直下の .keelsonignore に書きます。.gitignore は読みません。

書き方意味
1 行 1 パターン一致するファイルやディレクトリを除外する。最後に一致した行が優先される
# で始まる行コメント
/、末尾 /、*、?、**、[abc]、[a-z]gitignore に似たパス・パターン指定
先頭 !一致するファイルやディレクトリを再び含める

!.env.production で env ファイルを意図的に含めることもできます。ただし、.git などの組み込み除外、.keelsonignore 自身、--secrets-from-env-file で指定したファイルは戻せません。

静的サイトで assets.dir の外側にあるファイルを含めたい場合も、先頭 ! を使います。ディレクトリとその中身を含めるには、!keep/ と !keep/** の両方が必要です。

keelson deploy --check --json を実行し、除外されたファイルと、秘密情報を含む可能性があるファイルを確認してください。

  • プレビュー用の認証情報やアプリ用トークンがファイル内で検出された場合は、アップロードを停止します
  • Webhook 署名鍵だけが検出された場合は、警告を表示して続行します
  • ファイル名などから秘密情報を含む可能性があると判断した場合も、警告を表示します。警告だけでは除外されないため、必要に応じて .keelsonignore に追加します

--check は主要な設定も手元で検証します。ただし、assets / crons / auth / secrets 内の未知のキー、cron 式の妥当性、verify のパス形式などは API 側で検証します。--check が通っても、デプロイ時に deploy.config.invalid で止まる場合があります。

JSON の項目確認できる内容
archive.excluded除外されたファイル
archive.excluded_env_files除外された env ファイル
archive.secret_like_files名前などから秘密情報を含む可能性があると判断されたファイル
archive.embedded_credentialsファイル内で検出された認証情報のパスと種別。値は表示しない
archive.unscanned_filesサイズ上限などにより本文を検査しなかったファイル
archive.unscanned_binary_countバイナリと判定され、本文を検査しなかったファイルの数

16 MiB を超えるファイルと、バイナリと判定されたファイルは本文を検査しません。アーカイブから除外されたファイルも検査対象外です。

secret_like_files には、credentials.json や *.pem のような名前のほか、過去に --secrets-from-env-file で指定したものの、今回は除外されていない実在ファイルも含まれます。prod-secrets.txt のように env ファイルの命名規則に当てはまらない名前も対象です。

過去に指定したファイルを判定するため、CLI はそのプロジェクト相対パスを .keelson-config/previous-secrets-files.json に保存します。この記録には秘密の値は含まれず、記録ファイル自体もアップロードから自動で除外されます。

ファイル名にコンテンツハッシュが入った JavaScript / CSS(例: index-B7hK2mQ1.js)は 1 年間キャッシュされます。Keelson は、ASCII 英数字と _ からなり英字と数字の両方を含む 8〜64 文字の末尾セグメントをハッシュと認識します。HTML、style.css や app.js のようなハッシュなしのファイル、画像などは、アクセスのたびに ETag で更新を確認します。

静的サイト(edge-static / edge-spa)の再デプロイは、新しいファイルがエッジに反映されるまで最大 60 秒ほどかかります(実測 1〜57 秒、中央値は 30 秒前後)。60 秒待ってからファイルの有無を判断してください。


デプロイ時のヘルスチェックに使うパスです。/ が重い、DB に依存する、などの場合に軽量なエンドポイントを指定します。

health:
path: /health
  • 省略時は / を確認します
  • / で始まり、.. を含まず、/__keelson で始まらない 2048 文字以内のパス
  • HTTP 2xx / 3xx / 404 で合格、それ以外の 4xx と 5xx で不合格。404 が通るため、合格は「起動した」ことの確認であり、そのパスが実装されていることの証明ではありません
  • 候補リビジョンの内部 URL へ直接送られ、公開ホストや認証は経由しません。公開パスの検証は verify で行います

アプリが必要とするシークレットと、必須の組み合わせを宣言します。値はここには書かず、コンソール、CLI、またはデプロイ時の env ファイルで設定します。

secrets:
items:
- name: OPENAI_API_KEY
description: "OpenAI API key"
- name: ANTHROPIC_API_KEY
description: "Anthropic API key"
required:
- any_of: [OPENAI_API_KEY, ANTHROPIC_API_KEY]
message: "AI プロバイダのキーを少なくとも 1 つ設定してください"
フィールド型必須既定値説明
itemslist—[]シークレットの定義
items[].namestringはい—名前。環境変数の形式(大文字英字か _ で始まり、A-Z0-9_ のみ、255 文字以内)で書く。KEELSON_ で始まる名前は予約
items[].descriptionstring—""用途の説明。コンソールに表示される
requiredlist—[]設定済みシークレットに対して検証するルール
required[].any_oflist条件付きnullいずれか 1 つが設定されていればよい名前の空でないリスト
required[].all_oflist条件付きnullすべて設定されている必要がある名前の空でないリスト
required[].messagestring—""未設定時に表示する案内

required の各エントリは any_of または all_of のどちらか一方だけを持ちます。参照できるのは items で宣言した名前だけです。

名前は値を設定するときに大文字に正規化され、required の照合は大文字小文字を区別します。name: api_key と書くと値は API_KEY として保存され、required の条件が満たされません。最初から大文字で書いてください。MY-KEY のようにハイフンを含む名前は宣言できても値を設定できません。

新しいアプリでは keelson deploy --new --secrets-from-env-file <path> で、アプリ作成・シークレット設定・初回デプロイを 1 回で行えます。既存アプリのシークレット変更は次のデプロイで反映されます。


Webhook の受信など、対話ログインを通さずに外部システムから呼ばれるエンドポイントを宣言します。

auth:
endpoints:
- /api/webhooks/stripe
- path: /api/external/status
methods: [GET]
  • auth がある場合、endpoints は空でないリストが必須です
  • 各エントリはパス文字列、または path と省略可能な methods を持つオブジェクト
  • パスは /api/external/ または /api/webhooks/ で始まり、重複しないこと。/__keelson 配下は不可
  • パスは前方一致です。/api/external/status は /api/external/status-extra にも一致します
  • methods を指定する場合は空でないリスト。省略時は全メソッドを許可。大文字小文字は区別しません
  • methods が効くのは /api/external/ だけです。/api/webhooks/ には適用されず、メソッドを絞りたければアプリ側で判定します

「対話ログインを通さない」は「Keelson の認証が不要」という意味ではありません。宣言しただけでは外部から届かず、呼び出し側は Keelson が発行する資格情報を付ける必要があります。

パス必要な資格情報
/api/external/...api スコープのアプリトークンを Authorization: Bearer で送る
/api/webhooks/...webhook スコープのアプリトークンを X-Webhook-Secret ヘッダーで送るか、/api/webhooks/<secret>/... の形の URL を外部サービスに登録する

トークンの発行は keelson apps tokens create、URL の組み立ては外部連携を参照してください。Stripe の署名だけを送る通常の Webhook や、アプリ独自の API キーだけを送るクライアントは、アプリに届く前に拒否されます。

これらのパスへのリクエストでは X-Keelson-User-Id は空文字になります(ヘッダー自体は常に付き、対話ユーザーがいなければ空)。空を「ユーザーなし」として扱ってください。Keelson の資格情報に加えて、アプリ側でも Webhook 署名や API キーによる検証を行ってください。


デプロイ後の検証で追加で確認するパスです。既定では / だけを確認します。

verify:
- /dashboard
- /settings
  • 単一のパスは文字列でも 1 要素のリストでも指定できます
  • 最大 20 パス(前後の空白を除き、重複を除いた数)。各パスは / で始まり、// で始まらず、パス要素としての .. と制御文字を含まないこと
  • コンテナ型・ハイブリッド型では公開ホスト経由で候補リビジョンから取得し、静的サイト・SPA では公開前にリリースマニフェスト上で解決します
  • どちらの種類でも、宣言したパスが 2xx / 3xx 以外(404 / 401 / 403 / 405 など)なら verification_declared_path_unreachable でデプロイに失敗します
  • verify: にはアプリ自身のログインを必要としないパスを書いてください。検証リクエストにはアプリのユーザー情報が付かないため、アプリが返す 401 / 403 も不合格です
  • / は verify: にも書かれていても従来どおり寛容です。API だけのアプリが / で 404 を返しても合格します

クライアント側ルーティングでしか到達できない画面など、/ から辿れないエントリを確認したいときに使います。

これは health.path とは別の判定です。health.path は候補リビジョンを内部 URL で直接確認し、プロセスが起動した証拠として 404 でも合格します。verify は宣言した公開パスが実際に配信できるかを確認するため、404 では不合格です。


新規アプリを作るときの配置リージョンを任意で指定します。

region: jp-tokyo

クラウド事業者のリージョン名ではなく、Keelson の論理キーを指定します。

論理キー表示名新規作成
jp-tokyo日本可(既定)
us-oregon米国西海岸未提供(指定すると拒否)

新規アプリでは CLI の --region、keelson.yaml の region、ワークスペースの既定リージョンの順で優先されます。明示したリージョンが未定義または新規作成に未開放の場合は、別リージョンへ切り替えずに拒否されます。

アプリのリージョンは作成時に確定し、後から変更できません。既存アプリと異なる region を指定したデプロイは拒否されます。別リージョンへ移す場合は、新しいアプリとして作り直してください。


storage は廃止された disk_id だけを受理します。値は無視され、永続ストレージや /data の同期を有効にしません。残っていたら削除してください。

フィールド型必須既定値説明
disk_idstring—null無視される旧識別子。小文字英数字とハイフン、1〜32 文字

デプロイモードは command、assets、assets.fallback の有無から自動的に決まります。keelson status --json などの出力では、下表の値が deploy_mode に返されます。静的サイト・SPA の設定には type: web も必要です。

呼称deploy_modecommandassetsfallback説明
Web アプリcontainerありなし—通常のアプリ
静的サイトedge-staticなしありなし静的ファイルのみ
SPAedge-spaなしありありフォールバック付き静的ファイル
ハイブリッドhybridありありtype: web を指定した場合は必須静的ファイル + バックエンド API

ルール内容
起動コマンド・定期実行ジョブ・静的サイトのいずれかが必要command、crons、または type: web と assets の組み合わせを指定する
タスクには投入するコードが必要tasks がある場合は command か crons を指定する(tasks_require_command_or_crons)
タスクとジョブの名前は別にするtasks[].name は crons[].name と同じにできない(task_name_conflicts_with_cron)
静的サイトと定期実行ジョブは併用不可静的サイトに必要な type: web と crons を併記すると、設定検証で拒否される。定期実行ジョブを使う場合は type を省略する
静的サイトにはコンテナがない静的サイト・SPA では db.migrate を宣言しても実行されない。マイグレーションを使う場合はコンテナ型かハイブリッド型にする
workers は廃止リクエストの後に実行する処理は tasks、決まった時刻に実行する処理は crons で書き直す
db.migrate は libsql のみdb.mode: libsql が必要
ローカル SQLite は一時領域のみdb.local_sqlite.paths は /tmp/ 配下か :memory:
mcp はサーバー側のプログラムが動くアプリだけcommand があり assets がないアプリにだけ書ける(mcp is only supported for container apps)
assets.api にはバックエンドが必要トップレベルの command を設定する
type: web を指定したハイブリッドには fallback が必要type: web、assets、command を併記する場合は assets.fallback を設定する。type を省略した場合は、この必須チェックの対象外

slug: crud-app
description: "顧客と案件を管理する社内 CRM"
runtime: python-slim
command: "python app.py"
db:
mode: libsql
migrate: "python migrate.py"
env:
PYTHONUNBUFFERED: "1"

手元で npm run build などを実行して dist を作ってからデプロイします。Keelson 側ではビルドしません。

slug: marketing-site
type: web
runtime: node-slim
db:
mode: none
assets:
dir: dist
fallback: index.html
slug: daily-report
runtime: python-slim
db:
mode: libsql
env:
PYTHONUNBUFFERED: "1"
crons:
- name: generate
schedule: "0 9 * * *"
command: "python report.py"
timeout: 120

Web アプリ + バックグラウンドタスク

Section titled “Web アプリ + バックグラウンドタスク”

Web アプリがリクエストを受けて SDK で generate-pdf を投入し、Keelson が Web アプリとは別のインスタンスでタスクのコマンドを実行します。詳しくはバックグラウンドタスクを参照してください。

slug: pdf-reports
runtime: python-slim
command: "python app.py"
db:
mode: libsql
env:
PYTHONUNBUFFERED: "1"
tasks:
- name: generate-pdf
command: "python generate_pdf.py"
timeout: 180
max_attempts: 3

Web アプリ + 未処理データを定期的に処理するジョブ

Section titled “Web アプリ + 未処理データを定期的に処理するジョブ”

タスクを使わずにリクエストの後の処理を組む場合は、未処理のデータを DB に保存して定期実行ジョブで処理します。処理は次の実行まで待ちます。リクエストの後すぐに始めたい処理には、バックグラウンドタスクを使ってください。

Web アプリが未処理のデータを Managed SQLite に保存し、定期実行ジョブが別のインスタンスで処理します。両者で共有するデータは db.mode: libsql に保存します。

slug: task-drainer
runtime: python-slim
command: "python app.py"
db:
mode: libsql
env:
PYTHONUNBUFFERED: "1"
crons:
- name: drain
schedule: "*/5 * * * *"
command: "python drain.py"
timeout: 120

ハイブリッド(静的ファイル + バックエンド API)

Section titled “ハイブリッド(静的ファイル + バックエンド API)”
slug: photo-galleries
runtime: node-slim
command: "node server.js"
db:
mode: libsql
env:
NODE_ENV: "production"
assets:
dir: dist
fallback: index.html
api: /api
slug: payment-hooks
runtime: node-slim
command: "npm start"
db:
mode: libsql
env:
NODE_ENV: "production"
secrets:
items:
- name: STRIPE_WEBHOOK_SECRET
description: "Stripe の Webhook 署名シークレット"
required:
- all_of: [STRIPE_WEBHOOK_SECRET]
auth:
endpoints:
- path: /api/webhooks/stripe
methods: [POST]

Stripe に登録する URL には webhook スコープのアプリトークンを含めます(https://<host>/api/webhooks/<token>/stripe)。methods は Webhook には適用されないので、POST 以外を拒否したければアプリ側で判定します。

slug: my-go-app
runtime: go-slim
command: "./app"
db:
mode: none

Keelson がデプロイ時に Linux 向けにビルドし、バイナリを ./app に置きます。


原因対処
db または db.mode がないdb.mode を libsql か none で宣言する
廃止された databases がある削除する。永続データは Managed SQLite、一時 SQLite は db.local_sqlite で宣言
workers がある(workers_not_supported)削除する。リクエストを受けて走らせる処理は tasks、時刻で動く処理は crons で書く
command に pip install / npm install がある(command_installs_dependencies)削除する。依存はビルド時に自動インストールされる
依存マニフェストがない(missing_dependency_manifest)Go ではプロジェクトルートに go.mod を置く。Python または Node.js では、外部ライブラリを使う場合に適切なマニフェストを置く。標準ライブラリだけなら不要
runtime と command の言語が合わない(runtime_command_mismatch)ランタイムを合わせるか、起動コマンドを直す
env の値が引用符なし(env_value_not_string)すべての値を引用符で囲む(数値・真偽値も)
環境変数名が KEELSON_ で始まる名前を変える。この名前空間はプラットフォーム予約
local_sqlite.paths が /tmp/ の外/tmp/、:memory:、または Managed SQLite を使う
起動コマンド・定期実行ジョブ・静的サイトの設定がないcommand、crons、または type: web と assets の組み合わせを追加する
crons[].timeout が 600 を超える600 以下、かつプランの上限以下にする
tasks があるのに command も crons も無い(tasks_require_command_or_crons)タスクを投入する Web アプリの command、または crons のジョブを追加する
タスクと定期実行ジョブの名前が同じ(task_name_conflicts_with_cron)どちらかの名前を変える
tasks[].timeout が 600 を超える(TASK_TIMEOUT_EXCEEDS_STANDARD_LIMIT)600 以下、かつプランの上限以下(plan_task_timeout)にするか、処理を小さなタスクに分ける
mcp.enabled が true なのに mcp.tools がないツールを 1 本以上宣言するか、mcp.file で別ファイルに置く
静的サイト・SPA・ハイブリッドに mcp がある(mcp is only supported for container apps)mcp を削除するか、command だけのアプリにする
mcp.tools[].input に $ref や x-mcp-header があるスキーマを展開して書く。$dynamicRef と $recursiveRef も使わない
mcp contains unsupported field(s): fileKeelson CLI が古い。keelson upgrade で最新版にする
assets.api が / で始まらない/api のような絶対パスにする
type: web を指定したハイブリッドで assets.fallback がないindex.html などを設定する
ビルドが reserved_path_conflict で失敗ビルド出力直下の __keelson ディレクトリを改名する

環境変数一覧

Keelson がアプリのコンテナに自動設定する環境変数と、ローカル開発で Keelson SDK を使うための環境変数をまとめています。

自動設定される変数は、keelson.yaml の env に書く必要はありません。自分で定義・上書きせず、Keelson が設定した値を使います。KEELSON_ で始まる名前は Keelson が管理する予約名で、env / secrets には使えません。

変数内容
PORTWeb アプリがリクエストを待ち受けるポート番号。起動時にこの値を読み取って使用します
TZワークスペースのタイムゾーン(例: Asia/Tokyo)
KEELSON_MODEKeelson 上では keelson。ローカル開発では通常は未設定。Files / Media SDK をローカル保存で使う場合や、Tasks SDK でタスクを手元で同期実行する場合は local も指定できます
KEELSON_APP_IDアプリの内部 ID
KEELSON_WORKSPACE_IDワークスペースの内部 ID
KEELSON_TENANT_IDKEELSON_WORKSPACE_ID の互換用別名(値は同じ)
KEELSON_DEPLOY_ID現在のデプロイの内部 ID
KEELSON_APP_URLアプリの公開 URL(https://<ws>--<app>.keelson.run)。絶対 URL が必要な場面で使います。Keelson がアプリの公開ホスト名を生成できる場合に設定されます
KEELSON_DIRECTORY_BASE_URLIdentity SDK が使う Directory API のベース URL。KEELSON_APP_URL と同じ値が、同じ条件で設定されます

既存アプリとの互換性のため、KEELSON_TENANT_ID にも KEELSON_WORKSPACE_ID と同じ値を自動設定します。旧名の廃止時期は未定です。

Files / Media / Identity / Tasks / Email SDK 用の接続先や認証情報は、各 SDK が自動で読み取ります。通常はアプリのコードから直接参照する必要はありません。

変数条件内容
KEELSON_DB_URLdb.mode: libsqlManaged SQLite の接続 URL
KEELSON_DB_AUTH_TOKENdb.mode: libsqlManaged SQLite の認証トークン
TURSO_DATABASE_URL / TURSO_AUTH_TOKENdb.mode: libsqlそれぞれ KEELSON_DB_URL / KEELSON_DB_AUTH_TOKEN と同じ値が設定される互換用の別名
KEELSON_FILES_BUCKET / KEELSON_FILES_PREFIXコンテナ型のデプロイで、ファイル保存先が構成されているとき(通常のデプロイでは自動)Files SDK が使う保存先
KEELSON_INTERNAL_MEDIA_BASE_URL / KEELSON_APP_MEDIA_TOKENコンテナ型のデプロイで、Media 機能が有効なとき(通常のデプロイでは自動)Media SDK が使うアップロード先と認証トークン
KEELSON_MEDIA_URL_PREFIXMedia SDK のアップロード先・認証トークンと同じ条件Media の配信 URL の先頭部分(/__keelson/media/)
KEELSON_DIRECTORY_TOKENkeelson apps directory enable を実行し、アプリを再デプロイした後Identity SDK が使う Directory API のトークン。サーバー側だけで扱います
KEELSON_EMAIL_API_URL / KEELSON_EMAIL_TOKENコンテナ型のデプロイ(通常のデプロイでは自動)Email SDK がメール送信に使う接続先と認証トークン。サーバー側だけで扱います
KEELSON_EMAIL_WEBHOOK_SECRETKEELSON_EMAIL_TOKEN と同じ条件メールの配信結果の通知の署名を検証する鍵。Email SDK の検証関数に渡します
KEELSON_TASKS_BASE_URLKeelson がバックグラウンドタスクを受け付けている間、Web・定期実行ジョブ・タスクの各コンテナに設定(tasks の宣言の有無は問いません)Tasks SDK がタスクの投入と状態の取得に使う URL。自分で設定する必要はありません

静的サイト・SPA(assets を指定し、command を省略する構成)にはコンテナがないため、実行時の環境変数は設定されません。Files / Media の SDK は静的サイトからは使えません。定期実行ジョブだけのアプリも command を省略しますが、ジョブはコンテナで動作し、SDK 用の環境変数を利用できます。

  • NODE_ENV — 自動では設定されません。keelson.yaml の env で "production" を宣言してください
  • 外部 DB の接続情報 — 自動では設定されないため、シークレットで渡します。db.mode: none の場合、Managed SQLite の接続情報は自動設定されません

SDK を手元の開発環境で動かすための設定です。必要に応じてローカルの環境変数として指定し、keelson.yaml の env や Keelson のシークレットには登録しません。

変数内容
KEELSON_MODE=localFiles / Media SDK でローカル保存を使用します。Tasks SDK では、enqueue が CLI の keelson dev task run でコマンドを手元で同期実行します。通常のローカル開発では未設定でも同じ動作になります
KEELSON_FILES_DIRFiles SDK のローカル保存先(既定 ./.keelson/files)
MEDIA_DIRMedia SDK のローカル保存先(既定 ./media)
KEELSON_LOCAL_MODE=1Identity SDK が API に接続せず、テスト用データを返すようにします。keelson dev serve 経由で起動するときは不要です(ローカルで動かす)
KEELSON_LOCAL_USERS_FILEKEELSON_LOCAL_MODE=1 のときに Identity SDK が読む名簿ファイル(既定 ./.keelson/dev-users.json)
KEELSON_LOCAL_USER_ID / KEELSON_LOCAL_USER_EMAIL / KEELSON_LOCAL_USER_NAMEローカル開発で Identity SDK が返すテスト用ユーザーの ID・メールアドレス・表示名。名簿ファイルがあるときは使いません
KEELSON_LOCAL_WORKSPACE_ID / KEELSON_LOCAL_WORKSPACE_ROLE / KEELSON_LOCAL_APP_IDローカル開発で Identity SDK が返すテスト用のワークスペース ID・ワークスペース内のロール・アプリ ID

Identity SDK は、互換性のため旧名の KEELSON_LOCAL_TENANT_ID と KEELSON_LOCAL_TENANT_ROLE も読み取れます。新旧両方を指定した場合は、WORKSPACE を含む新しい名前の値が優先されます。旧名の廃止時期は未定です。

CLI コマンド

keelson CLI の主なコマンドと、スクリプトやエージェントから使うときに知っておくべき挙動をまとめています。通常は AI エージェントが Skill を通じて実行するので、すべてを覚える必要はありません。各コマンドの使い方は keelson <コマンド> --help でも確認できます。

インストールはクイックスタート、更新は keelson upgrade で行います。

以下のコマンド例では、<...> は指定する値、[...] は省略できる引数やフラグを表します。<duration> には 30s や 5m のような期間、<n> には整数を指定します。

複数のコマンドで使えるフラグです。対応状況はコマンドによって異なり、login / logout / install-agent / dev ... などでは使えるフラグが限られます。app curl は --json と --quiet を受け付けません。

フラグ内容
--app <slug>対象アプリ。省略時はカレントディレクトリの keelson.yaml から解決。access show / access set / apps tokens create / apps tokens rotate / feedback は省略できない
--workspace <slug|name|id>対象ワークスペースを slug、名前、ID のいずれかで指定する。同名のワークスペースがある場合は slug か ID を使う
--json結果を JSON で出力。スクリプトやエージェントから使うときに
--quiet説明や進捗を抑える。出力内容はコマンドによって異なる
--timeout <duration>API 呼び出しのタイムアウト。通常は既定 15 秒。例外は下記を参照
--retry <n>一時的な失敗の再試行回数。通常は既定 0 回。例外は下記を参照
--no-browserブラウザを自動で開かず、ログイン・承認用 URL を表示する。login / apps delete / db apply / snapshots restore で使える

ワークスペースは、--workspace → keelson.yaml の workspace → 保存済みの既定ワークスペース、の順に選ばれます。ここまでで対象が決まらない場合、所属先が 1 つならそれが選ばれ、複数なら --workspace での指定が必要です。apps list は例外で、省略時は全ワークスペースを対象にします。

--quiet を付けると、secrets set はシークレット名ごとに 1 行、deploys list はデプロイ ID ごとに 1 行を出力します。deploy --check は成功時に何も出力しません。

タイムアウトと再試行回数には、次の例外があります。

  • apps duplicate のタイムアウトは既定 120 秒です。
  • app curl のアプリへのリクエストはタイムアウトが既定 130 秒で、GET / HEAD の一時的な失敗は最大 2 回再試行します。--retry 0 で再試行を無効にできます。
  • deploy --new のアプリ作成処理は、--retry 0 でも一時的な失敗に対して最大 3 回再試行します。この処理の再試行回数は 3 回未満に設定できません。--retry に 3 より大きい値を指定すると、その回数まで再試行します。

preview / app curl は、ブラウザでの承認が必要な場合も --no-browser を受け付けません。非対話実行ではブラウザを自動で開かず、承認用 URL を出力します。preview --json でも同様です。

--json でコマンドが失敗すると、標準出力に error オブジェクトが返ります。code はエラーコード、message は説明、hint は対処方法です。retryable が false の場合は自動再試行せず、hint に従って対処してください。

--tenant や keelson tenants list などの旧名も受け付けます。コマンドの別名を参照してください。

コマンド内容
keelson login [--no-browser]ブラウザでログインし、この端末を紐付ける
keelson logout [--all-devices]この端末の保存済みログイン情報を削除し、サーバー側のセッションを失効させる。--all-devices ですべての端末をログアウト。KEELSON_TOKEN で渡したトークンは失効しない
keelson whoamiログイン中のユーザーと、現在選ばれているワークスペース。ワークスペースの slug を確実に取るには workspaces list --json を使う
keelson workspaces list [--query <text>] [--exact]所属ワークスペースの一覧・検索。--json の slug が --workspace に渡す値
keelson doctor [--fix-path]PATH・ログイン・Skill の状態を確認。--fix-path はシェルの設定ファイルに PATH を追記
keelson install-agent <claude-code|codex|cursor> [--global]AI エージェントに Skill を入れる。引数なし + --yes で入れてある Skill をすべて更新
keelson upgradeCLI を最新版に更新
keelson versionバージョン表示
keelson telemetry status|off|onCLI の利用状況データの状態確認、停止、有効化
keelson feedback --app <slug> [--deploy <id>] < feedback.jsonCLI や Skill の使い勝手を標準入力の JSON で送る。形式はフィードバックを参照

keelson install-agent の代わりに、公開リポジトリからスキルをインストールすることもできます。Claude Code では /plugin marketplace add keelsonhq/skills に続けて /plugin install keelson@keelson-skills を実行します。ほかのエージェントでは npx skills add keelsonhq/skills を使います。インストール方法はどれか 1 つだけを使ってください。組み合わせると、同じスキルが 2 つ入ったり、互いに上書きしたりすることがあります。これらの方法でインストールされるのはスキルだけなので、CLI のインストールとログインも必要です。

Claude Code プラグインとして入れたスキルは keelson login では自動更新されず、Claude Code のプラグイン更新で追随します。プラグインは keelson doctor の検査対象外の場所にあるため、Claude Code が「not configured」と表示され、install-agent を案内されても従う必要はありません。

コマンド内容
keelson deployカレントディレクトリのアプリをデプロイ。完了まで待つかどうかは下の表を参照
keelson deploy --new [--region <region>] [--secrets-from-env-file <path>]アプリを作成して初回デプロイ。env ファイルの値をシークレットとして登録し、そのファイルはアーカイブから除外
keelson deploy --check [--json]アップロードせず、設定の検証とアーカイブ内容(除外ファイル、秘匿値らしきファイル)を表示。ローカル検証なので、プランの上限に当たるかどうかまでは分からない
keelson deploy --ndjson --yesスクリプト向け。進捗を 1 行に 1 つの JSON オブジェクトとして出力する
keelson status [deploy_id] [--wait] [--max-wait <duration>]指定したデプロイの状態を表示する。ID を省略するとアプリの最新デプロイが対象になる
keelson diagnose [deploy_id]ID を指定するとデプロイを診断する。省略すると稼働中アプリを診断する。起動失敗時は保存済みのアプリの起動ログも表示する
keelson deploys list [--limit <n>] [--offset <n>]デプロイ履歴
keelson deploys download <deploy_id> [-o <path>]そのデプロイに使ったソースアーカイブをダウンロード(保持期間内のみ)
keelson rollback [deploy_id] [--yes]前の成功デプロイ、または指定した成功デプロイに戻す
実行のしかた挙動
対話端末で keelson deploy完了まで待つ(既定 30 分。--watch-timeout で変更)
パイプや CI など非対話で keelson deploy --yes受け付けた時点で返る。待つなら --yes --watch
--json受け付けた時点で返る。--watch と併用不可
--ndjson完了まで待つ(--no-watch と併用不可)
--no-watch対話端末でも受け付けた時点で返る

通常の出力モードでは、非対話実行に --yes が必要です。省略すると confirmation_input_unavailable エラーになります。--json / --ndjson / --quiet を指定した場合は、確認を求めずに実行します。

待ち時間の上限を超えても監視をやめるだけで、サーバー側のデプロイは止まりません。status --wait で続きを確認できます。

status --wait の待機上限は既定 50 秒です。--max-wait <duration> で変更できます。上限に達すると、その時点で取得できた最後の状態を返します。デプロイが未完了でも、状態の取得に成功すれば終了コードは 0 です。

スクリプトでは、終了コードだけでデプロイの成否を判断せず、--json の terminal と status を確認してください。terminal: false なら未完了です。成功の確認には terminal: true かつ status: "completed" が必要です。

最新デプロイが失敗していても、前の成功リビジョンが動いている場合があります。現在配信しているアプリの状態は keelson app info で確認してください。

戻せる対象は、コンテナイメージを持つ成功デプロイです。静的サイトは、以前のソースを再デプロイして戻します。ロールバックではビルドを省略しますが、db.migrate は再実行されます。

rollback は受け付けた時点で戻ります。返されたデプロイ ID を keelson status <deploy_id> --wait --json に渡し、terminal と status を確認してください。--json または非対話実行では --yes が必要です。

NDJSON では、stage と status などを持つ進捗行が流れます。例えばヘルスチェック中は stage が health_check、status が progress になります。進捗行の status だけでデプロイ全体の成否を判定しないでください。

  • 監視まで進んだ場合、最後に result を持つ行が出ます。値は success または failed です。
  • 監視開始前に失敗した場合は、error を持つ行が出て、result 行は出ません。設定の不備などではエラー行だけですが、シークレットの登録やアーカイブのアップロード後に失敗すると、それ以前の進捗行も出ています。
  • プロセスの終了コードも確認してください。0 以外は CLI の処理が失敗したことを示します。監視のタイムアウトでも失敗扱いになりますが、サーバー側のデプロイは続いている場合があります。
コマンド内容
keelson app curl <path> [-i] [--method <method>] [--data <body|@file>] [--form <name=value|name=@file>] [--header <name:value>]認証付きでアプリにリクエストを送る。デプロイ後の確認
keelson preview [--ttl <duration>] [--allow-writes] [--json]アプリへの認証付きアクセスに使う、有効期限付きのトークンを発行する
keelson app info / start / stop / restartアプリの情報表示、起動、サスペンド、再起動。restart は完了まで待つ(--no-watch で受付後に返る)

app curl で --data を指定するときは、--method に POST / PUT / PATCH / DELETE のいずれかも指定してください。--form を指定し、--method を省略した場合は POST になります。複数のフォーム項目やヘッダーを指定する場合は、--form や --header を繰り返します。

レスポンス本文は標準出力に出ます。-i を付けると、HTTP ステータスとレスポンスヘッダーも標準エラー出力に表示します。GET / HEAD の一時的な失敗は既定で最大 2 回再試行し、--retry 0 で無効にできます。

preview と app curl は、現在配信しているデプロイを行った本人以外が実行すると、読み取りだけでもブラウザでの承認が必要です。この承認では、ブラウザで承認しただけではトークンの発行やリクエストの送信は行われません。承認後 30 分以内に、同じコマンドへ --confirmation <id> を付けて再実行してください。下記の「ブラウザでの承認が必要な操作」とは手順が異なります。

コマンド内容
keelson logs app <slug> [--mode live|history] [--severity <level>] [--previous] [--limit <n>] [--since <duration>](--previous は現行ランタイムでは未対応で 422 を返す。クラッシュした旧インスタンスの出力は通常のログに含まれる)アプリの標準出力・標準エラー
keelson logs cron <slug> [--severity <level>] [--limit <n>] [--since <duration>]cron の実行ログ
keelson logs access [--limit <n>] [--since <duration>]アクセスログ
keelson logs deploy [deploy_id] [--limit <n>] [--since <duration>]デプロイの進行ログと保存済みの失敗詳細。アプリが起動中に終了した場合は、アプリの起動ログも表示。成功時のビルド出力は保存されないため、表示できるログがなければ理由を表示

--since には 1h や 1d12h のような期間を指定します。指定できる範囲は 1 秒〜30 日です。

--json は、ログ配列などを含む 1 つの JSON オブジェクトを出力します。ログの格納先はコマンドによって異なります。

コマンドJSON 内のログ
logs app / logs cronentries 配列。各要素は時刻、重大度(severity)、メッセージなどを持つオブジェクト
logs accessentries 配列。各要素は時刻、HTTP ステータス、パスなどを持つオブジェクト
logs deploylogs 配列に進行ログのオブジェクトを格納。failure_logs と startup_logs は文字列の配列

値はコマンド引数ではなく標準入力または env ファイルから渡します。

コマンド内容
keelson secrets list設定済みの名前と更新日時。値は表示しない
echo -n "<value>" | keelson secrets set <KEY> [--apply]1 件設定。--apply で再デプロイまで要求(受付後に返る)
keelson secrets set --from-env-file <path> [--apply]env ファイルからまとめて設定。key ごとに保存するので、途中で失敗すると一部だけ保存された状態になる(エラーに保存済みの key が入る)
keelson secrets unset <KEY>削除

シークレットは次のデプロイ(または --apply)で反映されます。

コマンド内容
keelson apps list [--workspace <slug>] [--query <text>] [--exact]アプリ一覧。--workspace を省略すると所属する全ワークスペースを対象にする。--json の結果は、指定時は apps 配列、省略時は workspaces 配列に格納される
keelson apps duplicate <slug> --name <new> [--copy-secrets] [--enable-schedule]アプリを複製。cron のスケジュールは既定で無効のまま複製
keelson apps rename <new_slug>公開 URL の slug を変える。旧 URL は 410 を返し、自動リダイレクトはされない。反映まで時間がかかる
keelson apps delete [slug]アプリを削除。ブラウザでの承認が必要
keelson quotasプランの上限と使用量

apps rename の実行後は、手元の keelson.yaml の slug も新しい値に変更してください。

複製は、元アプリの最新の成功したデプロイを基に作られます。複製先は、独自の URL とデプロイ履歴を持つ別のアプリです。

項目複製時の扱い
アプリのコード最新の成功したデプロイから複製する
設定 (keelson.yaml)複製先の新しいアプリ識別情報を除いて複製する
定期実行ジョブの定義複製するが、複製先のデプロイが成功した後に停止状態で作られる。--enable-schedule を付けると有効になるが、設定で enabled: false を明示したジョブは停止状態のまま
アプリ単位のシークレット既定では複製しない。--copy-secrets を付けると複製する
ワークスペース単位のシークレット複製しないが、ワークスペース内の全アプリに渡されるため複製先でもそのまま利用できる
プラットフォーム管理の資格情報複製先用の新しい資格情報を発行する
Keelson 管理 DB の中身複製せず、新しい空の DB を作る。CLI には DB の中身を複製するオプションはない
デプロイ履歴複製しない。元アプリの最新の成功したデプロイを基に、複製先の新しいデプロイを作る
コマンド内容
keelson crons list [--include-disabled]ジョブ一覧
keelson crons trigger <name> [--wait]今すぐ実行。--wait で実行結果まで待つ
keelson crons enable <name> / disable <name>有効化 / 無効化
keelson crons runs list [--cron <name>] [--limit <n>] [--offset <n>]実行履歴
keelson dev cron run <name> [--file <path>] [--dir <path>]keelson.yaml の cron をローカルで 1 回実行する
keelson dev task run <name> [--payload <JSON> | --payload -] [--file <path>] [--dir <path>] [--json]keelson.yaml のバックグラウンドタスクをローカルで 1 回、同期で実行する(再試行なし)。--payload はタスクの payload を JSON で渡す(- で標準入力から読む。省略時は null)。--json では標準出力に結果の JSON を 1 つだけ出す。手元で enqueue したときに SDK が使うコマンド。バックグラウンドタスクを参照
コマンド内容
keelson dev serve [--port <n>] [--users <file>] [--as <id>] -- <command...><command> でアプリのサーバーを起動し、その手前で Keelson と同じユーザー情報のヘッダーを付けて中継する。利用者は切り替えページ(/__keelson/dev)や X-Keelson-Dev-As ヘッダーで切り替える。--port は待ち受けるポート(既定 5173)、--users は名簿ファイル(既定 ./.keelson/dev-users.json、無ければ組み込みの 4 人)、--as は起動時の利用者。アプリには空いているポートを PORT で渡し、<command> 中の {port} も置き換える。デプロイ先の環境では起動しない。ローカルで動かすを参照
keelson dev serve --check何も起動せずに終了コード 0 で終わる。この CLI が dev serve に対応しているかの判定に使う
コマンド内容
keelson db apply [file.sql]Managed SQLite に SQL を実行(省略か - で標準入力)。既にテーブルなどがある DB へはブラウザでの承認が必要。テーブル・インデックス・ビュー・トリガーを 1 つも持たない初回の DB には承認なしで適用される
keelson snapshots list復元ポイントと、実行中・直近のエクスポートの一覧。エクスポートは成功・失敗したものも含む
keelson snapshots create手動スナップショット
keelson snapshots download <id> [-o <file>]バックアップのダウンロード。-o を省くとダウンロード URL を表示するだけでファイルは書かない
keelson snapshots export --at <RFC3339>任意時点(PITR)のエクスポートを作成。完了後に download で取得
keelson snapshots restore <id>復元。ブラウザでの承認が必要。72 時間以内なら取り消し可
keelson snapshots restore-status最新の復元の進捗と結果を確認
keelson db revoke-tokens [app] [--yes]DB の認証トークンを失効させる(破壊的)。端末内で確認を求める。非対話端末では --yes。失効後、新しいリビジョンに切り替わるまで DB は一時的に使えない
keelson db recover [app] / credential-status [app]認証情報の復旧と状態
コマンド内容
keelson groups listグループ一覧
keelson groups create <key> [--name <text>] [--description <text>] [--if-not-exists]カスタムグループを作成
keelson groups members list <key>グループのメンバー
keelson groups members add|remove <key> <email>...既存メンバーの所属を変更(招待ではない)
keelson access show --app <slug>アプリの閲覧 / 管理グループ
keelson access set --app <slug> [--view <key>]... [--manage <key>]... [--allow-self-lockout]閲覧・管理権限を持つグループを設定する

access set は、指定した種類の権限をすべて置き換えます。--view だけを指定した場合、管理権限は変更しません。閲覧権限を空にするには --view none を指定します。管理権限は空にできません。

複数のグループを指定する場合は、フラグを繰り返してください。例えば、閲覧グループを sales と engineering に変更するには次のように実行します。

Terminal window
keelson access set --app my-app --view sales --view engineering

自分の管理権限が失われる変更には --allow-self-lockout が必要です。

コマンド内容
keelson apps tokens create --app <slug> --name <name> --scope <scope> [--scope <scope>]... [--allowed-ip <cidr>]...アプリトークンを発行(1 回だけ表示)。--app は省略できない
keelson apps tokens list一覧(メタデータのみ)
keelson apps tokens rotate --app <slug> --token-id <id>ローテーション。旧トークンは猶予付きで失効
keelson apps tokens delete <token_id>失効
keelson apps directory enableDirectory API のトークンを発行し、KEELSON_DIRECTORY_TOKEN として登録(要再デプロイ)
keelson domains listカスタムドメインの一覧と状態
keelson domains add|verify|remove <hostname> [--yes]カスタムドメインの追加・検証・削除(Team)。remove は非対話端末では --yes

--scope に指定できる値は次のとおりです。受信用と Directory 用は同じトークンに混在できません。

scope用途
api外部システムからアプリの /api/external/... を呼ぶ
webhook外部サービスからアプリの Webhook を受ける
directory:members:read / directory:identity:read / directory:groups:read / directory:group-members:readアプリから Directory API を呼ぶ(通常は apps directory enable が付与する)

keelson feedback --app <slug> [--deploy <id>] < feedback.json は、CLI と Skill の使い勝手についてのフィードバックを送ります。標準入力に JSON オブジェクトを 1 つ渡します。定義されていないフィールドは拒否されます。

フィールド内容
verdict(必須)score と summary を持つオブジェクト。score は 1〜10 の整数、summary は 1〜1,000 文字の総評。両方とも必須
friction困った点を記録するオブジェクトの配列。最大 20 件。各要素のフィールドは下記を参照
yaml_confusions / error_message_reviews / wanted_commands / kept_goodそれぞれ文字列の配列。最大 20 件
skill_gaps / verification_method文字列

friction の各要素には、次の 6 フィールドがすべて必要です。

フィールド型・内容
step文字列。困った場面や作業の段階
command文字列。実行したコマンド
output_quote文字列。表示されたメッセージの引用
expected文字列。期待していた動作
lost_minutes0〜1,440 の整数。対処にかかった分数
recovered真偽値。解決できた場合は true、未解決なら false

例えば、次の内容を feedback.json に保存して送信できます。内容は実際の体験に合わせて書き換えてください。

{
"verdict": {
"score": 8,
"summary": "デプロイできたが、非対話実行時の確認方法で迷った。"
},
"friction": [
{
"step": "CI からのデプロイ",
"command": "keelson deploy",
"output_quote": "The confirmation response could not be read from standard input.",
"expected": "非対話実行に必要なフラグを事前に知りたかった。",
"lost_minutes": 3,
"recovered": true
}
]
}

次の旧名も受け付けます。撤去時期は未定ですが、正式な名前のほうを使ってください。別名は --help に独立したコマンドとしては出ません。

別名正式な名前
keelson backups list / trigger / download / restorekeelson snapshots list / create / download / restore
keelson tokens list / deletekeelson apps tokens list / delete
keelson skill install [--global] [--codex] [--yes]keelson install-agent <claude-code|codex|cursor> [--global] [--yes]
keelson tenants listkeelson workspaces list
keelson db rotate-credentialskeelson db revoke-tokens
--tenant--workspace

ブラウザでの承認が必要な操作

Section titled “ブラウザでの承認が必要な操作”

アプリの削除、既存スキーマがある DB への SQL の実行、スナップショットからの復元は、CLI だけでは完了しません。コマンドは承認用 URL を表示し(confirmation_required)、ブラウザで承認すると操作がサーバー側で実行されます。コマンドを再実行する必要はありません。承認の期限は 15 分です。

URL を表示したあとの挙動は実行のしかたで変わります。

  • 対話端末: 既定で承認と実行の完了まで待つ。--wait=false で待たずに返る
  • --json または非対話端末: 既定で待たずに返る。--wait を付ければ待つ

preview / app curl の承認は、これとは別の再実行方式です(「確認」の節を参照)。rollback / db revoke-tokens / domains remove はブラウザ承認ではなく端末内の確認で、非対話端末では --yes が要ります。

変数内容
KEELSON_TOKENログインの代わりに使うトークン(CI 向け)。保存済みログインより優先。設定してあると空文字でもエラーになる
KEELSON_NO_UPDATE_CHECK=1 / KEELSON_NO_SKILL_SYNC=1更新確認 / Skill の自動更新を止める(KEELSON_NO_SKILL_SYNC は空でなければ 0 でも止まる)
KEELSON_NO_TELEMETRY=1CLI の利用状況データを止める
KEELSON_CONFIG_DIR設定ファイル(credentials.json / config.json)を置くディレクトリ
KEELSON_API_BASE_URL / APP_DOMAIN_BASE接続先 API とアプリドメインの上書き(通常は不要)

エラーコードと対処方法

Keelson で発生する主なエラーと、その確認方法・対処方法をまとめています。CLI のエラーに加え、コンソールで確認できるデプロイの失敗や、公開したアプリへのアクセス時のエラーも扱います。

CLI のエラーでは code と message、対処の案内がある場合は hint を確認してください。CLI や API でデプロイの失敗を確認する場合は、failure_code と failure_hint を確認します。案内が表示されている場合は、個別の状況に応じた内容なので、このページより優先してください。AI エージェントに調査や修正を依頼するときも、これらの情報を渡すと役立ちます。

このページは全コードを網羅していません。掲載のないコードが出た場合も、表示されたメッセージや対処の案内を確認してください。

操作した場所によって、エラーを確認する画面や応答の形式が異なります。

確認する場所確認方法・応答の形式
コンソールアプリの概要やデプロイ履歴で、失敗したデプロイの理由とログを確認
CLIターミナルのエラーメッセージを確認。--json を付けると、標準出力の error に code、message、hint、retryable などが入る
デプロイの失敗(keelson status / keelson diagnose)failure_code と failure_hint を確認
Keelson API通常は detail にメッセージ。入力検証エラーでは detail が配列になる場合もある。承認が必要な操作、スナップショット、入力検証、管理権限などに関する一部のエラーでは、error に code、message、hint などが入る
公開したアプリへのアクセス拒否(ゲートウェイ)ブラウザには案内画面。fetch / XHR には {"detail":"ip-restricted"} などの JSON。X-Keelson-Auth-Error ヘッダーで拒否の理由を確認できる
稼働枠不足・停止中(ゲートウェイ)HTTP 503。x-keelson-platform-error ヘッダーに capacity-full / app-suspended。稼働枠不足では X-Keelson-Error-Code: CAPACITY_FULL も返る
存在しない URL・停止中・予約パスへのアクセス(エッジ)HTTP 404 / 410 / 503 / 403。ブラウザには案内画面、API 等には平文を返す。一部の予約パスはブラウザにも平文を返す。理由は x-keelson-platform-error ヘッダーで確認する。ゲートウェイに届かないため X-Keelson-Auth-Error は付かない
Files / Media の配信x-keelson-error ヘッダーで理由を確認。一部のコードでは error、hint、doc を含む JSON も返る

CLI の --json で返るエラーの例です。

{
"error": {
"code": "not_logged_in",
"message": "Not logged in.",
"hint": "Run `keelson login` or set KEELSON_TOKEN, then retry the command.",
"retryable": false
}
}

retryable は、同じ操作を再試行すると成功する可能性があるかを示します。HTTP ステータスだけでは判断できません。同じ 503 でも false の場合があり、ビルドの 429 にはトライアルの累積上限のように待っても解除されないものがあります。

アップロード前のチェック結果です。keelson deploy --check でも確認できます。エラーがあるとデプロイは中止されます。警告だけの場合は続行できます。

依存マニフェストとは、使用するライブラリを宣言するファイル(go.mod、requirements.txt、package.json など)です。Go では外部ライブラリを使わなくても go.mod が必要です。Python と Node.js では、依存マニフェストがなくても警告にとどまります。

コード原因対処
missing_dependency_manifest選択したランタイムの依存マニフェストがないGo ではプロジェクトルートに go.mod を置く。Python または Node.js で外部ライブラリを使う場合は、その依存関係を宣言するファイルを置く
command_installs_dependenciescommand に pip install / npm install / go build がある削除し、起動コマンドだけにする
runtime_command_mismatchruntime と command の言語が合わない(例: python-slim で node)どちらかを直す
assets_dir_emptyassets.dir が無い、空、またはパスの大文字小文字が違うビルドを実行する。パスを確認する
assets_fallback_missingassets.fallback のファイルが assets.dir にないビルド出力を確認する
db_wiring_fail(警告)db.mode とコードの DB 接続が食い違う(本番の接続先が KEELSON_DB_URL を読んでいない、ローカル開発用の file: フォールバックが本番経路に残っている、宣言との不一致など複数の規則がある。file:local.db を環境変数未設定時のフォールバックとして持つこと自体は問題ない)表示された hint に従う。多くは KEELSON_DB_URL を読む libSQL クライアントに直す
file_wiring_fail(警告)状態ファイルやアップロードをローカルディスクに書いているFiles / Media SDK に置き換える
icon.pathicon のファイルが無い、読めない、シンボリックリンク、またはアップロードから除外されるアップロードされる場所に画像を置き、keelson.yaml からの相対パスで icon を書く。icon を参照
icon.format / icon.size / icon.dimensions / icon.corrupticon の画像が PNG・JPEG・WebP でない、256KB を超える、縦横が 32〜2048 ピクセルの外、またはヘッダーが読めない条件に合う PNG・JPEG・WebP で書き出し直す

keelson.yaml とアップロード内容の検証

Section titled “keelson.yaml とアップロード内容の検証”

次のエラーがあるとデプロイできません。

コード原因対処
invalid_keelson_configYAML が不正、db.mode が libsql / none 以外、db.migrate を none と併用、KEELSON_ で始まる env、/__keelson 配下のパス、不明な runtime、cron が 10 本超、などmessage の内容を直す
config_missing_field必須フィールドがない(slug、runtime、db など。静的サイトでは type と assets も)追加する
env_value_not_stringenv の値が文字列でない、YAML が曖昧に解釈するキー、キーの重複、merge key(<<)値は引用符かブロック文字列にする。曖昧なキー(on / yes / 数字など)は引用符で囲む。重複は削除する。merge key は展開して書く。重複と merge key は引用符で囲んでも拒否される
description_too_longdescription が 300 文字超短くする
workers_not_supportedworkers: がある削除する。リクエストを受けて走らせる処理は tasks、時刻で動く処理は crons で書く
DB_DATABASES_REMOVEDdatabases: がある削除する。永続データは db.mode: libsql
SQLITE_LOCAL_SQLITE_INVALID_PATHdb.local_sqlite.paths が /tmp/ の外/tmp/ か :memory: にする
CRON_TIMEOUT_EXCEEDS_STANDARD_LIMITcrons[].timeout が 600 秒超600 秒以内にするか、処理を分割する
plan_cron_limit / plan_cron_min_interval / plan_schedule_timeoutcron の本数・実行間隔・timeout がプランの制限に合わない本数を減らす、実行間隔を長くする、timeout を短くする。または必要な条件を満たすプランに変更する
tasks_require_command_or_cronstasks があるのに command も crons も無く、タスクを投入するコードが無いタスクを投入する Web アプリの command、または crons のジョブを追加する
task_name_conflicts_with_crontasks[].name が crons[].name と同じ(前後の空白を除いて小文字化した後で比べる)タスクかジョブの名前を変える
TASK_TIMEOUT_EXCEEDS_STANDARD_LIMITtasks[].timeout が 600 秒超600 秒以内にするか、処理を小さなタスクに分ける
plan_task_timeout明示した tasks[].timeout がプランの 1 回の実行時間の上限を超えるtimeout を短くするか、上限の大きいプランに変更する。timeout を省略した場合は拒否されず、プランの上限に切り下げられる
ARTIFACT_SQLITE_FILE_DRIVERファイル SQLite クライアント(sqlite3、better-sqlite3 など)が検出されたlibSQL クライアントへ移行。一時用途なら db.local_sqlite で宣言

ビルド出力の直下に __keelson ディレクトリがあるとビルドが失敗します。このときの failure_code は deploy.build.failed で、ビルドログに reserved_path_conflict が出ます。ディレクトリ名を変えてください。

アップロード内容に関する警告

Section titled “アップロード内容に関する警告”

次のコードは警告であり、これだけでデプロイが失敗することはありません。処理の目的に応じて対応してください。

コード原因対処
ARTIFACT_EXEC_NODE_CRON / ARTIFACT_EXEC_APSCHEDULERアプリのプロセス内で定期処理を登録しているアプリがスリープすると実行されないため、定期処理を独立したコマンドに切り出し、keelson.yaml の crons に定義する
ARTIFACT_EXEC_BACKGROUND_TASKSレスポンスを返した後に実行する処理がある短い外部呼び出しや、リクエストの成功に必要な処理は、レスポンスを返す前に await して完了を待つ。レスポンスの後に続ける処理は tasks に宣言して SDK で投入する(バックグラウンドタスク)
ARTIFACT_EXEC_SET_INTERVALサーバー側のコードで setInterval を使っている実行が抜けても問題のないキャッシュ更新などは変更不要。継続的な定期実行が必要な処理は crons に移す

コンソールのアプリ概要・デプロイ履歴で、失敗の理由とログを確認できます。CLI の keelson status / keelson diagnose や API では、失敗コードを failure_code、対処の案内を failure_hint で確認します。デプロイ開始時に操作を受け付けられなかった場合のコードも、下表に記載しています。

コード原因対処
deploy.config.invalidkeelson.yaml の内容が不正指摘されたキーを直して再デプロイ
deploy.config.secret_missingsecrets.required で宣言したシークレットが未設定keelson secrets set <KEY>(値は標準入力)で全て設定してから再デプロイ
deploy.artifact.invalidアップロードしたアーカイブの検証に失敗した(必要なロックファイルの不足など)エラーメッセージで指摘されたファイルやコードを直す
deploy.artifact.assets_invalidassets.dir / assets.fallback で宣言したファイルがアップロードに含まれていない手元でビルドしてから再デプロイ
deploy.build.failedビルド失敗(依存の解決失敗、ビルドスクリプトのエラー、予約パスの衝突)keelson logs deploy <deploy_id> でビルド出力の末尾を見る
deploy.build.timed_outビルドが制限時間内に終わらなかった不要な依存を減らす、重い前処理をビルドの外に出す
deploy.build.image_too_largeイメージが大きすぎる不要な依存やファイルを減らす
deploy.runtime.start_failedアプリが起動中に終了、またはコンテナが起動状態にならなかったkeelson logs deploy <deploy_id> で保存済みのアプリの起動ログを読み、PORT と 0.0.0.0 を確認
deploy.runtime.health_check_failed起動・ヘルス検証の制限時間内に、/(もしくは health.path)から期待する応答(2xx / 3xx / 404)を得られなかった。アプリが起動中に終了した場合や、401 / 403 / 5xx を返し続ける場合を含むkeelson logs deploy <deploy_id> で保存済みのアプリの起動ログを読み、待ち受けポートと health.path を確認
deploy.runtime.migration_faileddb.migrate が 0 以外で終了。旧リビジョンが配信を継続マイグレーションを直して再デプロイ。途中まで適用された DB の変更は自動では戻らない
deploy.verify.failedverify のパスが取得できない、または JS / CSS の Content-Type が不一致パスとビルド出力を確認。詳細は failure_logs の verification_* コードに出る
deploy.plan.limit_exceededプランの上限を超えているfailure_hint で対象の上限を確認し、設定や使用量を上限内に収める。または必要な上限のあるプランに変更する
deploy.plan.cron_count_exceededcron の本数がプラン上限を超えているcron の本数を減らすか、より多く登録できるプランに変更する
deploy.plan.cron_interval_too_shortcron の間隔がプランの最小間隔より短い実行間隔を長くするか、より短い間隔で実行できるプランに変更する
deploy.plan.schedule_timeout_exceeded明示した crons[].timeout がプラン上限を超えているtimeout を短くするか、必要な実行時間に対応したプランに変更する
deploy.app.suspendedアプリまたはワークスペースが Keelson 側の制限措置を受けているサポートへ連絡する。利用者や管理者による再開では解除されない
deploy.app.operation_in_progressこのアプリで別の操作が進行中だったため、デプロイを開始できなかった別の操作が完了してから再デプロイ
deploy.platform.temporarily_unavailable一時的な障害待ってから再試行する。追加で 3 回まで。それでも失敗するなら deploy_id を添えてサポートへ
deploy.platform.errorプラットフォーム側のエラー再試行しても直らない。deploy_id を添えてサポートへ
deploy.cancelledデプロイが取り消された必要なら再デプロイ
deploy_in_progress(409)別のデプロイまたはアプリ操作が進行中進行中の操作が完了してから同じコマンドを再実行
build_rate_limit_exceeded / build_guardrail_exceeded(429)ビルド回数・ビルド時間・同時実行・連続失敗の上限メッセージが解除条件を示す。連続失敗は原因を直して記載の時刻を待つ。トライアルのビルド枠は累積で、待っても解除されないので、その場合はプランを変更する
saved_config_unsupported(409)再デプロイ / ロールバック対象の keelson.yaml が現在のスキーマに合わない直して通常のデプロイをする

定期実行ジョブの失敗・スキップ

Section titled “定期実行ジョブの失敗・スキップ”

keelson crons runs list --json の executions[].failure_code で確認できます。失敗の調査には keelson logs cron で実行ログも確認してください。

cron.failed.* は開始したジョブの失敗、dispatch_failed はジョブを起動できなかったことを示します。cron.skipped.* は、条件を満たさないため予定されていた実行をスキップしたことを示します。

コード意味
cron.failed.command_failedコマンドが 0 以外で終了した
cron.failed.timed_outtimeout を超えた
cron.failed.terminated_no_callback実行が終了したが結果が報告されなかった
dispatch_failed実行の起動に失敗した
cron.skipped.schedule_disabledジョブが無効化されている
cron.skipped.quota_exceededプランの実行枠を超えた
cron.skipped.due_to_lock / cron.skipped.lease_held前回の実行がまだ終わっていない
cron.skipped.missed_run予定時刻を過ぎたため実行をスキップした
cron.skipped.app_suspendedアプリが停止中
cron.skipped.plan_downgradeプラン変更で実行できなくなった

バックグラウンドタスクのエラー

Section titled “バックグラウンドタスクのエラー”

アプリが enqueue や get を呼んだときに、Tasks SDK が返すエラーです。SDK のエラーは言語ごとに 1 種類(Python と Node.js は TasksError、Go は *tasks.Error)で、code・status・message を持ちます。分岐は code で行い、message の文言に頼らないでください。使い方はバックグラウンドタスクを参照してください。

コード意味出る場面対処
TASK_INVALID_REQUESTタスク名が空、idempotency_key が 1〜128 文字の印字可能 ASCII でない、payload を JSON にできない、などの不正な要求enqueue(本番・手元)引数を直す。同じ呼び出しを繰り返しても通らない
TASK_NOT_DECLAREDタスク名が、現在デプロイされている tasks に無い(手元では keelson.yaml に無い)。tasks から外した名前を含むenqueue(本番・手元)tasks に宣言してデプロイするか、名前を直す
TASK_NOT_FOUNDtask_id が存在しないか、別のアプリのタスク。手元の get は同じプロセスで投入したタスクしか知らないget(本番・手元)アプリが保存した task_id を確かめる
TASKS_UNAVAILABLEタスクの受付が閉じている、またはアプリがサスペンド・隔離・削除中。繰り返しても通らないenqueue(本番だけ)コンソールでアプリの状態を確認する。Keelson が制限したアプリはサポートに連絡する
TASK_PAYLOAD_TOO_LARGE投入要求の body(payload と idempotency_key を JSON にしたもの)が 65,536 bytes を超えるenqueue(本番・手元)大きな入力はデータベースかファイル保存の SDK に置き、その ID を payload で渡す
TASK_BACKLOG_LIMIT_EXCEEDEDアプリの未処理(queued)のタスクが 1,000 件に達しているenqueue(本番だけ)未処理のタスクが減るのを待つか、投入の頻度を下げる。投入済みのタスクには影響しない
TASK_MONTHLY_QUOTA_EXCEEDEDワークスペースの月間枠「バックグラウンド実行」で、1 回目の試行の分を確保できないenqueue(本番だけ)確保済みの枠が返されたとき・プランを上げたとき・翌 UTC 月初のいずれかで通るようになる。同じ月の中でも通ることがあるので、翌月まで止まると決め打ちしない。SDK は自動で再試行しない
TASKS_UNAVAILABLE_TRANSIENT一時的な障害(エラー本文の無い 502 / 503 / 504、接続の失敗、1 回あたり 15 秒の時間切れ)enqueue・get(本番だけ)SDK が合計 3 回まで試す。enqueue は idempotency_key があるときだけ再試行するので、重複を作らないようキーを付ける
TASKS_UNAUTHORIZEDruntime API が ID トークンを受け付けなかった(401)enqueue・get(本番だけ)続く場合はサポートに連絡する
TASKS_FORBIDDENCloud Run が呼び出しを拒否した(403)。tasks を宣言した最初のデプロイの直後は、権限の反映に数分かかることがあるenqueue・get(本番だけ)最初のデプロイから数分待って試し直す。続く場合はサポートに連絡する
TASKS_SERVER_ERRORエラー本文の無い、その他の 5xxenqueue・get(本番だけ)時間をおいて試し直す。enqueue を試し直すときは idempotency_key を付ける
TASKS_HTTP_ERRORエラー本文の無い、その他の失敗の応答enqueue・get(本番だけ)続く場合はサポートに連絡する
TASKS_UNEXPECTED_RESPONSE成功の応答が決められた形でなかったenqueue・get(本番だけ)SDK を更新する。続く場合はサポートに連絡する
TASKS_IDENTITY_TOKEN_ERRORメタデータサーバーから ID トークンを取れなかったenqueue・get(本番だけ)続く場合はサポートに連絡する
TASKS_NOT_CONFIGUREDSDK が本番と手元のどちらで動かすかを決められなかった(KEELSON_MODE が不明な値、KEELSON_MODE=keelson なのに KEELSON_TASKS_BASE_URL か KEELSON_APP_ID が無い、など)enqueue・get本番では KEELSON_MODE などの KEELSON_ 変数を上書きしない。手元では KEELSON_MODE=local にするか Keelson の変数を外す。環境変数を参照
TASKS_LOCAL_CLI_NOT_FOUNDPATH に keelson が無いenqueue(手元だけ)https://keelson.dev/install.sh で CLI を入れる
TASKS_LOCAL_CLI_FAILEDCLI がタスクを実行できなかった(keelson dev task run の無い古い CLI、中断など)enqueue(手元だけ)keelson upgrade を実行する。keelson dev task run <name> --payload '<JSON>' で直接エラーを確かめる

コマンドが 0 以外で終わった・時間切れになったことは enqueue のエラーではありません。タスクは failed で終わり、get の last_failure_code に理由が入ります。

タスクの失敗理由(last_failure_code)

Section titled “タスクの失敗理由(last_failure_code)”

get は、failed のタスクと、再試行を待っているタスクに last_failure_code を返します。最後の試行の標準エラーは、コンソールのアプリ画面の「バックグラウンドタスク」タブで確認できます。

値意味試行回数に数えるか再試行
exit_nonzeroコマンドが 0 以外の終了コードで終わった数える試行回数が残っていればする
timed_out試行が timeout を超え、Keelson がコマンドを打ち切った数える試行回数が残っていればする
no_completion結果の連絡が無いまま期限を過ぎた数える試行回数が残っていればする
delivery_failed試行を 1 時間以内に開始できなかった数えないしない
manifest_unresolvable待っている間にデプロイが 2 回以上重なり、投入時の宣言を使えなくなった数えないしない
quota_exhausted再試行、または月をまたいで開始する試行で、月間枠「バックグラウンド実行」を確保できなかった数えないしない

手動の再実行はありません。もう一度実行するには、アプリから投入し直してください。

keelson snapshots ... のエラーです。snapshot_export_ で始まるコードは、snapshots export で発生します。

コード意味対処
snapshot_plan_not_included手動スナップショットがプランに含まれない(現在は全プランで利用できるため、通常は発生しない)サポートに連絡する
snapshot_no_managed_dbManaged SQLite を使っていないdb.mode: libsql でデプロイし、DB を作成する
snapshot_db_name_invalid / snapshot_db_name_unknownDB 名が不正、または存在しないhint に出る名前を使う
snapshot_already_running / snapshot_export_already_running前の処理が実行中終わってから再試行
snapshot_rate_limited / snapshot_export_rate_limited回数上限hint の時刻まで待つ
snapshot_export_invalid_timestamp復元可能な範囲の外の時刻範囲内の時刻を指定する
snapshot_app_deleting / snapshot_export_app_deletingアプリが削除中—
snapshot_launch_failed / snapshot_export_launch_failed起動に失敗再試行。続くならサポートへ
snapshot_disabled / snapshot_storage_unconfigured(snapshot_export_ で始まるコードも同様)機能が無効、または保存先が未設定サポートへ連絡する
snapshot_not_readyまだダウンロードできない(X-Keelson-Error-Code ヘッダー)完了を待つ

コマンドの実行時に表示される主なコードです。

コード意味対処
not_logged_in未ログインkeelson login
multiple_workspaces / workspace_ambiguous / workspace_not_foundワークスペースを特定できない--workspace <slug> で指定する。keelson workspaces list --query <検索文字列> で検索できる
no_workspaces利用できるワークスペースがないコンソールでワークスペースを作成する
app_not_foundアプリが見つからないslug を確認。新規なら --new
app_manage_requiredアプリは存在するが、管理権限がない現在の管理者に、管理権限を持つグループへ自分を追加してもらう。ワークスペースの OWNER / ADMIN なら keelson groups members add <グループ> <メール> で自分を追加する
app_deleting削除中のアプリ完了を待つ
forbidden権限がない。CONSOLE_ONLY_OPERATION ならコンソール限定の操作ロール・アプリ権限を確認
confirmation_expired / confirmation_rejected承認の期限切れ(削除・SQL・復元は 15 分、preview / app curl は 30 分)/ 却下コマンドをやり直す
deploy_failedデプロイ失敗keelson diagnose <deploy_id>
sql_faileddb apply の SQL エラー。ロールバック済みSQL を直す
usage(access set。API の 409 detail.reason が last_manage_binding_removed / no_effective_manager / self_lockout)管理グループが空になる、有効な管理者がいなくなる、自分の権限が失われる管理グループを残す。--allow-self-lockout で解除できるのは自分が外れる場合だけで、管理グループや有効な管理者がゼロになる変更は解除できない
transient一時的なネットワークエラー--retry 3 のように再試行回数を指定して実行

旧コードとの互換性: ワークスペースに関する一部のコードでは、旧 tenant 名のコードを互換用の error.aliases に含めます。旧コードの撤去時期は未定です。

ブラウザでの承認が必要な場合

Section titled “ブラウザでの承認が必要な場合”

confirmation_required は、操作を進めるためにブラウザでの承認が必要なことを示します。表示された URL を開いて承認してください。

  • 削除・SQL・復元は承認と同時に実行されるため、コマンドの再実行は不要です。
  • preview / app curl は、承認後に --confirmation <id> を付けて同じコマンドを再実行してください。

AI エージェント向け Skill の更新案内

Section titled “AI エージェント向け Skill の更新案内”

meta.skill_outdated または keelson doctor の skill_outdated 警告は、インストール済みの Skill が CLI より古いことを示します。この警告だけでコマンドが失敗することはありません。keelson install-agent --yes で更新してください。

公開したアプリにアクセスしたときのエラー

Section titled “公開したアプリにアクセスしたときのエラー”

以下は公開したアプリを開いたときの案内です。コンソール自体のエラー画面ではありません。

表示状態対処
ログイン画面未ログインワークスペースに登録されたアカウントでログイン
「このアプリへのアクセス権限がありません」(403)メンバーでない、ブロック済み、アプリの閲覧権限がない管理者にメンバー登録・権限を依頼
「このアプリは、許可されたアクセス元からのみ利用できます」(403)IP 制御で拒否。画面に送信元 IP が表示される管理者に、表示された IP アドレスを許可するか確認してもらう
「アプリの稼働枠が空いていないため現状アプリが起動できません。」(503)同時に使えるアプリ数の上限通常は、枠を使っている別のアプリへのアクセスが約 5 分途絶えると空く。枠が空かない場合は、管理者にアプリの停止やプラン変更を相談する
「アプリを起動しています」スリープからの起動中自動で画面が切り替わるまで待つ
「このアプリは停止されています」(503)管理者による停止、または Keelson 側の制限措置管理者に停止理由を確認する。管理者が再開できない場合はサポートへ連絡する
「アプリが見つかりません」(404)URL に対応するアプリが見つからない。URL の誤り、アプリの停止・削除、作成直後の公開待ちなどURL とアプリの状態を確認する。作成直後なら少し待つ
「この URL は利用できません」(410)公開 URL が変更された(旧 URL は 30 日間予約)管理者に現在の URL を確認する

API や XHR から同じ状況になると、ゲートウェイの拒否では JSON の detail と X-Keelson-Auth-Error ヘッダーに理由(ip-restricted、app-view-denied など)が入ります。稼働枠不足と停止中は x-keelson-platform-error ヘッダー(capacity-full、app-suspended)です。

アプリへのリクエストで返る HTTP エラー

Section titled “アプリへのリクエストで返る HTTP エラー”

次のエラーは、アプリへのリクエストや接続で発生します。特に WebSocket 接続の 501 は、そのままブラウザの案内画面として表示されるものではなく、利用者への表示はアプリ側のエラー処理によって異なります。

HTTP ステータス原因アプリ開発者の対処
504アプリが 120 秒以内に応答ヘッダーを返し始めなかった。ストリーミング全体の時間制限ではない応答までの処理時間を短くする。時刻や間隔を指定して実行できる定期処理なら crons に移す
501Keelson が WebSocket に対応していないSSE やポーリングを使う

外部連携(アプリトークン / Webhook)

Section titled “外部連携(アプリトークン / Webhook)”

ゲートウェイで認証・認可が拒否された場合は、詳しい理由が X-Keelson-Auth-Error ヘッダーで返ります。IP 許可範囲による拒否でも、互換性のため JSON の detail は machine-forbidden または webhook-forbidden のままです。

ゲートウェイでは、アプリの経路が見つからない場合と、トークンが別のアプリのものである場合を、machine-forbidden / webhook-forbidden にまとめて返します。一方、ゲートウェイに届く前にエッジで URL の経路が見つからない場合は 404 になります。

X-Keelson-Auth-Error原因
invalid-app-tokenトークンが無効、または失効済み
machine-scope-deniedトークンに api スコープがない
machine-endpoint-deniedauth.endpoints で宣言されていないパス・メソッド
machine-forbidden / webhook-forbiddenゲートウェイでアプリの経路が見つからない、またはトークンが別のアプリのもの
machine-ip-not-allowedアプリトークンの許可 IP の外から呼ばれた
invalid-webhook-secretWebhook シークレットが一致しない
webhook-scope-deniedトークンに webhook スコープがない
webhook-ip-not-allowedWebhook トークンの許可 IP の外から呼ばれた

/api/webhooks/email などプラットフォーム予約のパスへの Webhook は、ゲートウェイより手前のエッジが 403 の平文で拒否します。このときのヘッダーは X-Keelson-Auth-Error ではなく x-keelson-platform-error: auth-webhook-endpoint-blocked です。

CLI の利用状況データ

Keelson は、どの操作が成功しているかを把握し、信頼性の問題を見つけて CLI を改善するために、CLI の利用状況データを収集します。この機能は既定で有効です。

  • サブコマンド名
  • 終了コードと、利用できる場合は公開済みの失敗符号
  • コマンドの所要時間
  • 使用したフラグの名前(値は含みません)
  • 対話端末(TTY)で実行されたかどうか
  • CLI のバージョン
  • CLI を起動したエージェントの種別(判別できない場合は不明として記録)
  • 一覧系コマンドでは、結果が 0 件、1 件、複数のいずれかだけ
  • 重複を除くためのイベントごとの識別子
  • コマンドを実行した日時
  • セッション識別子。端末側で作る乱数で、コマンドが 30 分記録されないと作り直されます。保存されるイベントの行には、アカウント、ワークスペース、アプリの識別子は含まれません。イベントの送信にはログイン済みの資格情報を使い、送信回数はアカウント単位で数えるため、送信経路自体は匿名ではありません。
  • アプリのソースコードやデプロイアーカイブの中身
  • ログ、標準出力、標準エラーの本文
  • 環境変数の値やフラグに渡した値
  • ファイルパス、ワークスペース名、アプリ名
  • keelson.yaml の本文
  • AI エージェントとの会話

設定を保存して停止するには、次を実行します。

Terminal window
keelson telemetry off

環境変数で停止するには、次を設定します。

Terminal window
KEELSON_NO_TELEMETRY=1

環境変数は保存済みの設定より優先されます。環境変数が設定されている間は、keelson telemetry on を実行しても有効にできません。停止すると未送信のイベント記録は削除され、現在の状態はいつでも keelson telemetry status で確認できます。

停止するのはイベントの記録と送信、および X-Keelson-Client ヘッダーです。通常の User-Agent ヘッダーには、停止後も keelson-cli/<version> として CLI のバージョンが含まれます。

初めて利用状況データを送信する直前に、対話端末で実行している場合だけ 1 行の告知を表示します。対話端末ではない自動実行では表示しません。

keelson feedback は、利用者が提供するフィードバックを共有するための別の仕組みです。明示的に実行し、フィードバック共有が有効な場合にだけ送信します。フィードバック共有は既定で無効です。CLI の利用状況データを有効または無効にしても、フィードバック共有の設定は変わりません。