Skip to content

KIZAMI Cloud(hosted mode) ​

対象: ロードマップ「SaaS トラック」(app.kizami.dev)。2026-08-31 設計、未着手。 要件は 要件定義書 §7(マルチテナント)と マルチテナントとテナント分離 を前提とする。

方針 — セルフホストと同じものが動く ​

KIZAMI Cloud は本リポジトリのコードそのものを運用者がホストして提供する。 SaaS 専用のコードは「登録・課金・テナント運用」の薄い制御レイヤに限定し、 勤怠機能そのものには分岐を作らない。

  • 信頼: 勤怠データは人事情報そのもの。「中身は公開リポジトリで全部読める」ことが 最大のセキュリティ説明になる。
  • 保守: 単独運用で2系統のコードベースは負債。SaaS がセルフホスト構成の動作保証を兼ねる。
  • ライセンス: AGPL-3.0 は著作権者自身のホスト提供を制約しない。SaaS レイヤも本体と 同じ AGPL でこのリポジトリに置く。

コードの置き場(判断点) ​

別リポジトリの制御プレーンに切り出す案も検討したが、本体側に管理 API の口を開ける 必要が生じて結局本体が汚れるうえ、単独運用で2リポジトリは負債なので採らない。 モノレポ内・フラグゲートとする(Cal.com 等の先例に同じ):

  • 既定値はすべて「SaaS 機能 OFF」。SIGNUP_MODE や Stripe 系の環境変数が無ければ 完全に眠り、セルフホスト体験を一切変えない。
  • 副産物として、セルフホスト派も「招待制ではなく自由登録制」の運用に同じコードを使える。

実行基盤 — Phase 1 は Kubernetes + PostgreSQL ​

案判定理由
k8s + PostgreSQL採用PG ダイアレクトは実装・CI 済み。DATABASE_URL=postgres://… を渡すだけで既存イメージが PG で起動し、マイグレーションも自動適用される(packages/db/src/migrate.ts の URL ディスパッチ)。
Cloudflare Workers + D1見送りD1 が BEGIN を拒否するため db.transaction() を使う全書込系が未対応(Workers + D1 対応)。解消は独立した工事であり、SaaS の立ち上げをそれに賭けない。
新規 VPS見送り既存クラスタに対して費用増のみで利点が無い。

テナントが増えた時点で Workers + D1 への移行を再評価する。migrate-data (SQLite→PG)の設計は逆方向にも流用できる。

構成要素:

  • 専用 namespace(自分用本番・デモとは完全分離)。PG は単一 StatefulSet + PVC で開始 (HA 構成は現段階では過剰)。
  • 暗号化鍵(KIZAMI_ENCRYPTION_KEY)は hosted 専用に新規発行し、他環境と共有しない。
  • バックアップ: 日次 pg_dump -Fc → オブジェクトストレージ、30日保持+月初世代。 復旧手順を文書化し、実際に restore を一度通してから公開する。
  • 可用性: 単一ノードである間は SLA を掲げず、規約に「ベストエフォート」を明記する。

サインアップ ​

  1. 登録フォーム(メール+パスワード+組織名)。CAPTCHA(Turnstile 等)必須、 既存のレート制限基盤に signup 用バケットを追加。
  2. メール確認: セッションと同じトークン作法(乱数+SHA-256 保存)。送信は通知基盤の SMTP 経路を流用。
  3. テナント作成: create-tenant CLI の中身(tenant-bootstrap)を API 化して呼ぶ。 システムプリセット・既定 work policy・初期管理者まで一括。slug は自動採番とし ユーザーに選ばせない(衝突・商標問題の回避)。
  4. 初回ログイン後は既存のオンボーディングツアーがそのまま動く。

Closed Beta の間は招待コード制(コードが無ければ signup は 403)。公開時にコード要求を 外すだけの作りにしておく。

課金 — Stripe Checkout + Customer Portal + Webhook ​

カード情報は一切保持しない。アプリ内に決済フォームは作らず、加入は Stripe Checkout、 変更・解約・カード更新は Customer Portal に委ねる。自前実装は Webhook 受信と 状態機械のみ。

  • シート計測: 「有効(deactivate されていない)ユーザー数」。metered ではなく licensed quantity を日次ジョブ+メンバー増減イベントで同期する(単純・請求が予測可能)。
  • プラン構造: 機能制限プランは作らない。法定機能(36協定アラート等)を上位プランに 閉じ込めるのは「労働者に不利な実装をしない」という本製品の方針と衝突する。 差別化は無料枠の人数・サポート水準・専有インスタンスで行う。具体の価格は運用者の 事業判断でありこの文書の範囲外。
  • テーブル: billing_customers / billing_subscriptions(tenant_id、Stripe ID 群、 plan、seats、status、猶予期限)。Webhook は event id を記録して冪等化。

執行(enforcement)— 打刻は止めない ​

勤怠は業務クリティカルで、支払いトラブルの巻き添えで労働時間の記録が欠けるのは 「1分を刻む」という製品思想に反する。制限は管理機能側に寄せる。

状態従業員(打刻・本人閲覧・申請)管理者(承認・締め・設定・招待)
正常 / トライアル中○○
支払い失敗(猶予期間)○○ + 全画面バナー
猶予超過 / 無料枠超過○(記録は守る)ロック(閲覧のみ)。招待不可
解約後エクスポート案内 → 一定期間後にテナント削除エクスポートのみ可

法務・運用の要件 ​

  • 必須ページ: 利用規約 / プライバシーポリシー / 特定商取引法に基づく表記 / セキュリティ説明(暗号化・分離・バックアップ・監査ログ)。
  • 個人情報の整理: テナント企業が個人情報取扱事業者、運用者は委託先相当。 @kizami/privacy-template(テナント→従業員向け雛形)の対になる 「運用者→テナント」文書が要る。
  • データポータビリティ: CSV / API / 給与ソフト向けエクスポートは実装済み。
  • 監視は既存の 可観測性 の構成に hosted 環境を追加する。 テナント数・シート数もメトリクス化する。
  • 運用者コンソールはまず CLI(テナント一覧・suspend・プラン上書き・退会処理)。 管理 UI は必要になってから。

フェーズ計画 ​

  1. Closed Beta(課金なし・招待コード制) — PG デプロイ+バックアップ/復旧手順、 self-serve signup、監視配線。出口条件: 運用者以外のテナントが実勤怠を1ヶ月回して 締めまで通ること。
  2. Billing — packages/billing(Checkout / Portal / Webhook / シート同期 / 執行)、 法務ページ、トライアル・無料枠。
  3. Public Launch — テナント退会(下記ギャップ1)、招待コード撤廃、 テナント別クォータ。

公開前に塞ぐべき既知のギャップ ​

  1. テナント一括削除(退会)が未実装 — member.erase は個人単位のみ。 全データエクスポート→物理削除の退会フローは公開ローンチのブロッカー (退職者データの保持と消去 の対になるテナント版)。
  2. 人ごとの所定労働時間(時短勤務) — 現状、work policy は kind(flex/fixed)ごとに テナント高々1本の共有で、「一般は所定8時間・育児時短の人だけ所定6時間」が 固定時間制・フレックスでは表現できない(apps/api/src/routes/members.ts の 設計コメントで将来課題として明示済み。シフト制は所定が日単位なので対応可能)。 育児・介護休業法23条の短時間勤務措置は中小でも普通に発生するため、 不特定多数に提供する前に対応が要る。DB(user_policy_assignments は work_policy_id 参照)とエンジン(standardDayMinutes は入力)は複数ポリシーを 既に許容しており、制約は API の get-or-create 設計と UI のみ。
  3. 時短フレックス(清算期間の契約枠が法定枠より短い) — エンジンに「契約枠」の 概念追加が必要で工事が大きい。需要が見えてから。
  4. テナント別クォータ — 既存レート制限は IP/メール軸のみ。API キー数・通知送信数 などテナント軸の上限を Public Launch までに追加。