ドキュメント トップ / STANDALONE_ARCHITECTURE
使い方ローカル前処理エンジンログイン / プランレビュアーポートフォリオ最適化マスタ整合性レビューCPOS 連携 (PAT)JSON 形式データ取扱方針デプロイCLI技術

加算マネージャー — 単体販売アーキテクチャ(CPOS 連携はオプション)

このドキュメントは「単体販売を基本、CPOS 連携をオプション」に転換した後の 認証・データ保存・匿名化の設計をまとめる。実装は本ブランチ (claude/kassan-manager-cpos-integration-gygjv7) で行う。

1. 方針(要件)

  1. 単体販売が基本、CPOS 連携はオプション。CPOS が未設定でも製品として成立する。
  2. CPOS のログイン・manifest 等は現行 CPOS の流儀に従う。CPOS ログインは 極力目立たせず、ログイン時は CPOS のデータ(事業所・職員・請求の匿名集計)を使う。
  3. CPOS ログイン以外のユーザーのデータは、ユーザーごとに保存する。保存先は コンテナ同梱の PostgreSQL を自前で起動して使う(Cloud SQL 等の外部マネージド DB は 使わず、設定項目を最小化する)。(従来は他事業所ぶんも含めすべて CPOS app-data に保存していた。)
  4. ユーザー名 + パスワードでユーザーを識別する(ローカル認証)。
  5. 無料利用の機能は廃止する。匿名アクセスの無料枠・「無料版」導線は撤去し、 利用にはログイン(購入済みライセンス)を必須とする。
  6. CPOS 連携時以外のユーザーは、ローカル LLM をダウンロードして端末内で個人情報を 匿名化・JSON 化し、匿名化済みデータだけをサーバへアップロードして解析する。

2. 認証(2 プロバイダ)

同じ封緘セッション cookie (kasan_session, AES-256-GCM) を 2 系統のログインで発行する。

provider 識別 保存先 位置づけ
local ユーザー名 + パスワード(scrypt ハッシュ) 同梱 PostgreSQL users 既定(単体販売)
cpos CPOS 共通ログインゲートウェイ(現行の流儀) CPOS 側 オプション・控えめに表示

3. データ保存(プロバイダ別バックエンド)

req.user.authProvider で保存バックエンドを切り替える。ルートのコードは AsyncLocalStorage(リクエスト単位でユーザーを保持)+ファサードで呼び出し側を変えずに振り分ける。

routes ──▶ services/persistence/index.js (facade)
                 │  currentUser().authProvider で分岐
                 ├─ 'cpos'  ▶ services/cpos/store.js      (CPOS app-data:kasan:*)
                 └─ 'local' ▶ services/persistence/sql-store.js (同梱 PostgreSQL, ユーザー別)

同梱 PostgreSQL(Cloud SQL 不要・設定不要)

Cloud SQL のような外部マネージド DB は使わず、アプリと同じコンテナに PostgreSQL を 同梱して起動時に自動セットアップする(services/persistence/embedded-postgres.js)。

スキーマ(app_config / users / app_records / app_attachments)は起動時に CREATE TABLE IF NOT EXISTS で用意する。app_records はユーザー別隔離キー owner_idresource で全リソースを 1 テーブルに格納し、CPOS app-data と同じドキュメント形状を返す。

運用上の注意(Cloud Run):

永続化のセットアップ手順(推奨: Firestore)

.env に 2 行足すだけ。Filestore・VPC コネクタ・複合インデックスは不要。

KASAN_DATA_BACKEND=firestore
KASAN_GCS_BUCKET=<PROJECT_ID>-kasan-attachments

一度だけのインフラ準備 (すべて Node/gcloud。シェルスクリプト不要):

# 1) Firestore (Native) が有効なことを確認 (CPOS と同一プロジェクトなら作成済み)
gcloud firestore databases list --project=$PROJECT

# 2) 添付ファイル用の GCS バケット
gcloud storage buckets create gs://$PROJECT-kasan-attachments \
  --project=$PROJECT --location=asia-northeast1 \
  --uniform-bucket-level-access

# 3) Cloud Run のサービスアカウントに権限
gcloud projects add-iam-policy-binding $PROJECT \
  --member=serviceAccount:<RUNTIME_SA> --role=roles/datastore.user
gcloud storage buckets add-iam-policy-binding gs://$PROJECT-kasan-attachments \
  --member=serviceAccount:<RUNTIME_SA> --role=roles/storage.objectAdmin

あとは通常どおり npm run deploy:cloudrun。Firestore バックエンドでは NFS マウント・VPC 下り・max-instances=1 の強制はすべて外れる。

保存先: kasan_records / kasan_attachments (実体は GCS) / kasan_users / kasan_usernames / kasan_config。クエリは等値条件のみで引くため 複合インデックスは不要 (並べ替えはアプリ側)。

Filestore の撤去 (旧方式から移行したら必ず行う)

Filestore Basic HDD は 最小 1TiB のプロビジョニング課金 (東京で月 200 ドル前後・ 空でも満額)。2026-07 の高額請求の原因。Firestore へ切替えたら残さないこと。

順序が重要 — 削除は firestore バックエンドでのデプロイが成功した後に行う。 gcloud run deploy は前リビジョンの NFS マウント・VPC コネクタ設定を引き継ぐため、 deploy-cloudrun.js は firestore バックエンド時に --clear-volumes --clear-volume-mounts --clear-vpc-connector で明示的に外す (2026-08-04 修正)。 修正前の版でデプロイしたまま先に Filestore/コネクタを削除すると、サービング中の リビジョンがインスタンスを起動できなくなり、全リクエストが Google Frontend の 500 (Server Error) になる (2026-08-04 に実際に起きた障害)。さらにコネクタ削除後は 引き継ぎ検証に失敗して再デプロイ自体が通らなくなる。その状態からの復旧は リビルド不要で次の 1 コマンド (現行イメージから残骸設定だけ外した新リビジョンを作る):

gcloud run services update kasan-manager --region=asia-northeast1 \
  --project=$PROJECT --clear-volumes --clear-volume-mounts --clear-vpc-connector
# 0) firestore バックエンドで再デプロイ済みであることを確認
#    (出力に volumes / vpc-access-connector が現れないこと)
gcloud run services describe kasan-manager --region=asia-northeast1 \
  --project=$PROJECT --format='yaml(spec.template.spec.volumes, spec.template.metadata.annotations)'

# 心配なら先にバックアップ (容量課金は安い)
gcloud filestore backups create kasan-fs-final \
  --file-share=kasan --instance=kasan-fs --instance-zone=asia-northeast1-b \
  --region=asia-northeast1 --project=$PROJECT

# Filestore インスタンスの削除 (これで課金が止まる)
gcloud filestore instances delete kasan-fs \
  --zone=asia-northeast1-b --project=$PROJECT

# VPC コネクタも Filestore 専用だったので削除
gcloud compute networks vpc-access connectors delete kasan-conn \
  --region=asia-northeast1 --project=$PROJECT

.env からは KASAN_PGDATA_NFS / KASAN_VPC_CONNECTOR を消す (残っていても Firestore バックエンドでは無視されるが、紛らわしい)。

旧方式のセットアップ手順(Filestore + VPC。非推奨・参考として残す)

以下は一度だけ実行するインフラ準備。作成後は .env に値を書けば npm run deploy:cloudrun が自動でマウントする。

# 変数(自分の環境に合わせて)
PROJECT=your-gcp-project-id
REGION=asia-northeast1
ZONE=asia-northeast1-b
NETWORK=default

# 1) Filestore(NFS)インスタンスを作成(最小 1TiB。BASIC_HDD)
gcloud filestore instances create kasan-fs \
  --project=$PROJECT --zone=$ZONE --tier=BASIC_HDD \
  --file-share=name=kasan,capacity=1TiB \
  --network=name=$NETWORK

# 作成された NFS の IP を控える(KASAN_PGDATA_NFS に使う)
gcloud filestore instances describe kasan-fs --project=$PROJECT --zone=$ZONE \
  --format="value(networks.ipAddresses[0])"

# 2) Cloud Run から Filestore(VPC) へ到達するための Serverless VPC アクセスコネクタ
gcloud compute networks vpc-access connectors create kasan-conn \
  --project=$PROJECT --region=$REGION --network=$NETWORK \
  --range=10.8.0.0/28

.env に設定(<FILESTORE_IP> は 1) の出力):

KASAN_PGDATA_NFS=<FILESTORE_IP>:/kasan
KASAN_PGDATA_MOUNT_PATH=/var/lib/kasan
KASAN_VPC_CONNECTOR=projects/your-gcp-project-id/locations/asia-northeast1/connectors/kasan-conn
# コネクタ名だけでも可: KASAN_VPC_CONNECTOR=kasan-conn
CLOUD_RUN_MEMORY=1Gi

あとは通常どおりデプロイ(--add-volume / --add-volume-mount / --vpc-egress は自動付与):

npm run deploy:cloudrun

手動で gcloud run deploygcloud run services update に付ける場合の同等フラグ:

gcloud run services update kasan-manager --project=$PROJECT --region=$REGION \
  --execution-environment=gen2 --max-instances=1 \
  --add-volume=name=pgdata,type=nfs,location=<FILESTORE_IP>:/kasan \
  --add-volume-mount=volume=pgdata,mount-path=/var/lib/kasan \
  --vpc-connector=kasan-conn --vpc-egress=private-ranges-only

補足: Filestore は最小 1TiB で費用が大きい。ごく小規模で永続化コストを避けたい場合は、 揮発運用(ボリューム無し)+定期バックアップ、または外部の小さな PostgreSQL を DATABASE_URL で指す選択肢もある(その場合 max-instances 制限は外れる)。

4. 匿名化(ローカル LLM ダウンロード)

5. 無料枠の廃止

6. CPOS 側(流儀に従う)

7. 環境変数(すべて任意 — 単体運用は無設定で動く)

変数 既定 用途
(なし) 同梱 PostgreSQL が自動起動するため、基本設定は不要
KASAN_SESSION_SECRET 自動生成 セッション cookie 暗号鍵。未設定なら DB に自動生成・保存
DATABASE_URL (空) 外部 PostgreSQL を使う場合のみ。設定すると同梱 PG は使わない
KASAN_EMBEDDED_PG 1 0 で同梱 PG を無効化しメモリ実装に(開発・テスト用)
PGDATA / KASAN_PG_SOCKET_DIR Docker 既定 データディレクトリ / ソケット
KASAN_LOCAL_AUTH_ENABLED true ローカル認証。CPOS 専用運用時のみ false
KASAN_SIGNUP_ENABLED true 新規登録の受付。招待制運用時は false
KASAN_CPOS_APP_TOKEN (空) CPOS 連携(任意

単体運用で必須の設定はない(永続化のための PGDATA ボリュームマウントはインフラ設定)。 CPOS 連携用の変数はすべて任意。

このドキュメントはリポジトリ docs/ 配下の Markdown を配信しています。