V5: アプリ化(ローカル連携)
ローカル Rust サーバーと一体で動く「アプリ」だからこそ実現できる機能群です。ブラウザの外(OS・ファイルシステム・ネイティブバイナリ)に踏み込むため、静的配信やコンテナ実行では提供されず、/api/capabilities で提供可否を判定します。ブラウザ GIS との差別化要因となる流れで、今後もこのテーマで拡張していきます。
☁️ クラウドストレージ連携(rclone)
管理(⚙)→ アプリ → クラウド タブから rclone 経由で Box / GoogleDrive / OneDrive / Dropbox / pCloud / MEGA / WebDAV / S3 に接続します。ローカルサーバー版のみの機能は管理ウィンドウの「アプリ」カテゴリ(rclone・クラウド・ドライブ・保存のサブタブ)に集約されており、静的配信・コンテナ実行ではカテゴリ自体が非表示になります。rclone タブは外部ツールの導入とリモート登録(OAuth)、クラウドタブは接続、ドライブタブは Windows ドライブ割当(net use)を担当します。2つの利用形態があります:
- Windows ドライブ割当 —
rclone serve webdavを localhost で起動しnet useでドライブ化(既定 K:)。エクスプローラーや Office・QGIS など他のアプリから通常のドライブとして使えます - アプリ内
cloud:スキーム — レイヤーURLにcloud:フォルダ/file.geojsonと書くと/api/cloud/fileプロキシ経由で接続中ストレージのファイルを読み込みます。HTTP Range を転送するため DuckDB-WASM 等の範囲読み経路でも利用でき、ドライブ割当なしで動作します
なぜ静的配信(HTTP サーバーのみ)では実現できないか: rclone の実行・ドライブ割当(net use)・cloud: の解決プロキシはすべてブラウザ外の処理であり、Box 等の OAuth 認可コードフローに必要な client_secret も静的 web には埋め込めません。そのため本機能はアプリ(ローカルサーバー)専用です。
クラウドストレージ連携(rclone)の詳細
Box Drive 等のクライアントがインストールできない環境でも、単体バイナリで動く rclone を介してクラウドストレージを利用する機能です。ローカルサーバー(local ティア)のみで有効で、/api/capabilities の cloudRclone で提供可否を判定します。コンテナ実行時は /api/cloud/* を公開しません。
この連携の意義: 静的 HTTP サーバーにデータを置くのではなく、認証・ACL・監査・バージョン履歴を備えたクラウドストレージをデータ基盤にできます。各ユーザーは自分の OAuth アカウントで接続するためプロバイダ側で権限が強制され、.kasc には cloud: パスのみが書かれ認証情報は共有されません。同じフォルダを K: ドライブ経由で Office・QGIS・エクスプローラーからも使えるため、資料や GIS データを一元管理するシステムを組みやすい構成です。
rclone の導入
- 自動検出の順序: 設定ファイル(
kasugai_canvas.cloud.jsonのrclonePath)→ PATH →C:\kasugai\rclone\rclone.exe - 未導入時は rclone タブで「インストール先フォルダ」を指定して「ダウンロードして配置」で公式配布物を取得(インストーラー不要の単体バイナリ)
- リモート追加は rclone タブで種別(Box / GoogleDrive / OneDrive / Dropbox / pCloud / MEGA / WebDAV / S3)を選ぶだけで、その種別名をリモート名として
rclone config createを起動し、ブラウザで OAuth 認証します。登録したリモートの選択・接続はクラウドタブで行います
ルートフォルダとフォルダURL
- 「ルートフォルダ」に指定したフォルダが接続の起点になります(空欄はリモート全体)
- フォルダURLを指定するとフォルダID起点(rclone の
root_folder_id)で接続します。権限や共有経路によってリモート内の見える位置が異なる環境間でも、全員が同じフォルダをルートとして参照でき、.kasc のcloud:パス指定を環境間で統一できます - 対応URL形式: Box の
…/folder/<ID>と共有リンク…/s/<slug>(Box API の shared_items でフォルダIDに解決)、Google Drive の…/folders/<ID>、OneDrive / pCloud の?id=パラメータ。その他の種別はフォルダパスで指定します
権限とプロジェクト保存
- 読み取り専用:
rclone serve webdav --read-onlyで起動し、プロキシ書き込みは 403 で拒否。意図しない書き込みを防げます - 読み書き:
--vfs-cache-mode fullで起動。ファイルのアップロード・削除・.kascのクラウド保存が可能
起動時の自動接続(インスペクタの cloud: 行)
- インスペクタに設定行
cloud: リモート名 | ルートフォルダ | perm=write | drive=Kを書くと、.kascの読み込み(起動時・プロジェクト切替・登録ボタン)のたびに/api/cloud/serveが自動実行されます。.kascに保存しておけば起動時に接続済みの状態で始まり、レイヤーURLのcloud:パスもそのまま使えます - ルートフォルダはパスでもフォルダURLでも指定でき、省略時はリモート直下です。
perm=writeで読み書き(省略時は読み取り専用)、drive=Kで Windows ドライブ割当まで自動化します。rclone が未導入なら接続前に自動インストールします(既定C:\kasugai\rclone。install=offで無効化) cloud: stop(またはdisconnect)は接続停止を意味します。行末の| offは「設定は残すが接続しない」です。管理 → アプリ → 保存 の「現在の設定をインスペクタに追記して接続」ボタンで、クラウドタブの入力中の設定からこの行を生成できます- ローカルサーバー版でのみ動作します(静的配信・Cloud Run では
/api/cloud/*が存在しないため自動的に何もしません)。既に同じ内容で接続済みなら再起動せず、指定が変わったときだけ再接続します
WEB版での挙動(cloud: 行は無視される)
- 静的配信(
web/のみの環境)ではGET /api/cloud/statusのプローブが1回だけ飛んで 404 → 早期リターンします。install・serve・stop・driveなど副作用のある API は一切呼ばれません(実機検証済み) - エラー表示・例外も発生せず、管理のアプリカテゴリ自体が非表示になります。レイヤー行など他の設定行の処理は通常通り動作します
なぜアプリ版専用が妥当か
/api/cloud/*は rclone のローカルプロセス起動(rclone serve webdav・net use・ファイルプロキシ)を行う API で、ブラウザ内では実現不可能です。ティア分けでも V5 機能=「ローカル Rust サーバーと一体で動くアプリだから実現できる機能群」と定義しており、静的配信・Cloudflare Pages/Workers・Cloud Run には同等の実行基盤がありません- レイヤーURLの
cloud:スキーム自体も/api/cloud/fileプロキシ依存のため、WEB版で接続だけ自動化しても意味がありません。cloud:参照は従来からアプリ専用で、自動接続も同じ境界に乗せるのが一貫した設計です
設計上の利点
.kascをアプリ版とWEB版で共有しても、cloud:行は WEB 側で無害にスキップされるだけなので、同一の設定ファイルを環境非依存で使い回せます- WEB版でクラウドストレージを読みたい要件が出た場合は、rclone ではなく別経路(CORS対応の WebDAV 直読み、Workers プロキシ経由の R2/KV 等)を別機能として設計します
主な API
GET /api/cloud/status … 検出状態・バージョン・リモート一覧・接続状態
POST /api/cloud/install … 指定フォルダへ rclone をダウンロード配置
POST /api/cloud/config … リモート追加(config create → OAuth)
POST /api/cloud/serve … WebDAV 起動(remote / root|フォルダURL / readOnly / drive)
POST /api/cloud/stop … 停止(ドライブ割当も解除)
POST /api/cloud/drive … ドライブ割当・解除(Windows)
GET /api/cloud/list … フォルダ一覧(rclone lsjson)
GET/PUT/DELETE /api/cloud/file … ファイルの読み書き・削除(Range 転送対応)
POST /api/cloud/localize … {project, scope:"shared"|"project"}。共有が既定。_local/ ワークスペースを生成し、複製 .kasc を相対参照に書換え(納品用)
制約・注意点
- ドライブ経由の読み書きは Windows WebClient の既定上限(約 50MB)を受けます。
cloud:スキーム(アプリ内プロキシ)はこの制限を受けません - ドライブ割当は Windows のみ。オフラインキャッシュはなく、WebDAV のロックは限定的なため同時編集は避けてください(Box 等のバージョン履歴で過去版には戻せます)
- rclone の OAuth トークンは rclone 側の設定ファイル(
%APPDATA%\rclone\rclone.conf)で管理されます
💾 ローカルファイル保存
ローカルサーバー版の /api/files API で projects/<id>/DATA/ へのファイル書き込み・一覧・削除ができます。ブラウザ側は File System Access API による DATA/ 直接保存も併存します(Chromium系のみ・選択フォルダへ直接書込み)。
利用例: 描画ルートの保存、インスペクターからのファイル追加(.geojson/.gpkg/.fgb/.parquet を選ぶと対応する行タイプのレイヤー行を自動追記)、ダウンロードしたデータのプロジェクト内配置など。静的配信では利用できません(サーバー API 依存機能)。
🌐 データ取得プロキシ(/api/fetch)と接続先許可リスト
GET/HEAD /api/fetch は CORS 非対応の外部データを取り込むためのプロキシです(duckdb: の proxy=on や AI の fetchData が利用。Range/HEAD 転送対応)。SSRF 対策として、プライベート・ループバック・リンクローカル等のアドレスへの接続は既定で拒否します(ホスト名は名前解決して IP で判定)。社内 LAN の GIS サーバー等からデータを取得したい場合は、exe 隣の kasugai_canvas.fetch.json に許可リストを記述します(リクエスト毎に読むため再起動不要)。
{"allow":["192.168.0.0/16","geoserver.city.local","127.0.0.1"]} … ホスト名・個別IP・IPv4 CIDR
許可リストは管理(⚙)→ その他 の「データ取得プロキシ」セクションからも1行1エントリで編集・保存できます(保存で即時反映)。設定 API は GET/PUT /api/fetch/settings で、ローカル実行時のみ公開されます。既定で拒否される範囲の詳細は セキュリティ対策 を参照してください。
📦 納品用ローカル化(_local ワークスペース)
管理(⚙)→ アプリ → 保存 で「共有(既定)」または「専用」を選び、「納品パッケージを生成(_local/)」を実行します。元のプロジェクトは変更せず、プロジェクトフォルダの隣に <プロジェクト名>_local/ ワークスペースを生成します。API は POST /api/cloud/localize {"project":"内部ID","scope":"shared"}(省略時も shared)、専用は scope:"project" です。対象はディスクに保存済みの .kasc であり、インスペクターの未保存編集ではありません。
ProjectA_local/ … 納品ワークスペース。このフォルダを ZIP にして渡す
├─ projects.json … 群マニフェスト(複製内 .kasc を開くだけで登録される)
├─ DATA/ … 共有スコープ
│ ├─ cloud/ … 取得元別サブフォルダ(生成物)
│ ├─ http/ … HTTP・HTTPS データと _sources.json(生成物)
│ │ ├─ 3dtiles/ … HTTP 由来の 3D Tiles タイルセット一式(_sources.json に記録)
│ │ └─ xyz/ … XYZ タイルの受け皿(タイル本体はユーザーが配置)
│ └─ local/ … 既存の共有 ../DATA の複製(../DATA/local/x で参照)
└─ ProjectA/ … 元プロジェクトの複製(kasc・project.json・DATA/ 含む)
├─ a.kasc … ../DATA/http/x.csv・DATA/local/x などの相対参照
└─ DATA/ … 専用スコープ
├─ http/ … 専用スコープの取り込み先(生成物)
└─ local/ … 元からローカルだったデータ(DATA/local/x で参照)
共有は _local/DATA/ に保存して参照を ../DATA/http/x に、専用は _local/<name>/DATA/ に保存して参照を DATA/http/x に書き換えます。元プロジェクトの DATA/ は DATA/local/、親の共有 DATA/ は _local/DATA/local/ に集約して複製し、既存参照も DATA/local/x・../DATA/local/x に書き換えるため、生成物(http/・cloud/)と元からあったデータ(local/)は別フォルダで混在しません。_local/ フォルダだけで全参照が解決します。再実行は _local/ を毎回作り直すので、前回の出力や別スコープの残りファイルは混ざりません(_local/ 内に置いた独自ファイルは消えます)。ZIP 納品は _local/ フォルダごと渡し、受け取り側は複製内の .kasc を開くだけで群登録されます。
DATA/cloud/<取得元ハッシュ>/: 接続中のクラウドルートをrclone copyでコピーします。別の接続先・ルートの同名ファイルが混ざらないよう取得元ごとに分け、共有時は../DATA/cloud/<取得元ハッシュ>/パスに書き換えます(未接続時はスキップ)。取得元の対応はcloud/_sources.jsonに保存しますDATA/http/(共有時は../DATA/http/):.kasc内の http(s) URL をサーバーがダウンロードして保存し、複製 .kasc の参照を書き換えます。対象はベクター系レイヤー行(geojson:/layer:/geoparquet:/flatgeobuf:/gpkg:/duckdb:/fly_geojson:)の URL フィールド、style=/qml=オプション、info:/legend:、およびsql:クエリ内の URL リテラル(同じ URL が他の行で取得済みなら同一ファイルを指します)。xyz:行のリモート{z}/{x}/{y}テンプレートはタイル本体を取得せず、ユーザーがタイルを配置する受け皿http/xyz/<name>/(配置手順の_README.txt同梱)を生成して参照をローカルテンプレートへ書き換えます(元 URL はhttp/_sources.jsonに記録)。ローカルのDATA/...タイル参照はディレクトリごとlocal/に複製されます。ベースマップ相当(base:行)は対象外ですDATA/http/3dtiles/<name>/:3dtiles:行のリモート URL はtileset.jsonを起点に、参照される外部タイルセット(.json)とコンテンツ(content.uri/contents[].uri)を再帰的に辿って一式取得します。ルート tileset.json のディレクトリ配下の参照は階層をそのまま保持し、ルート外・別オリジンの参照は_ext/に集めて参照元 JSON の uri を書き換えます。複製 .kasc の参照はローカルの tileset.json を指します。対応はhttp/_sources.jsonに保存します。ベースマップ相当のサービス(tile.googleapis.comの Google Photorealistic 3D Tiles・assets.cesium.com/api.cesium.com等の全球・キー依存サービス)と implicit tiling は対象外で URL をそのまま残します。1タイルセットの取得上限は 10,000 ファイル・1ファイル 256MB です
同一 URL の重複参照は1ファイルにまとめます。http/_sources.json の出典一覧と既存ファイルを確認し、別 URL の同名衝突(大文字小文字違いを含む)はハッシュ接尾辞で回避します。ダウンロード失敗時は元の URL を残し、応答の failed に記録します。outputPath はワークスペースのパス、projectDir・dataPath は実際の保存先、dataReference は複製 .kasc 基準の相対パスを返します。
sql: のファイル参照も read_csv_auto('../DATA/http/x.csv') のように相対パスで保存し、描画・属性検索時に現在のプロジェクトから解決します。納品先の登録IDに依存しません。HTTP(S)の保存先は http/ に統一し、旧保存先との互換維持は行いません。スコープを切り替えると _local/ 内の出力先が変わるだけで、元プロジェクト側のファイル移動は行いません。
これにより「クラウド・Web 上のデータを正とした構築 → 納品時にローカル完結パッケージ化」という流れができます。取得済みデータの参照には元のクラウドの認証情報は不要です。ただし対象外のベースマップ(base:)やベースマップ相当の 3D Tiles、受け皿のみ生成した XYZ タイル本体、取得失敗した参照、CDN依存資産などは別途必要であり、完全オフライン動作を保証するものではありません(クラウドの cloud: 接続行は自動接続を防ぐためコメント化されます)。
安全対策として、..・バックスラッシュ・制御文字を含む cloud: 参照はプロジェクト外に出るため書き換え対象外です(クラウド参照のまま残ります)。大容量フォルダではダウンロードに時間がかかる点に注意してください。元プロジェクトは変更されないため、再実行するたびに最新の .kasc から URL を再取得してワークスペースを作り直します(クラウド・元URL側の更新は再実行で反映されます)。
📂 .kasc ファイル関連付け(外部プロジェクト)
インストーラー版は .kasc 拡張子をアプリに関連付けます。.kasc ファイルをダブルクリックすると、そのファイルを外部プロジェクトとして登録し、そのプロジェクトを開いた状態でブラウザが起動します(起動済みなら実行中のインスタンスに /api/projects/register で登録を引き継ぎます)。
- その場所がプロジェクトルート:
.kascのあるフォルダがそのまま/projects/<id>/の配信元になり、DATA/等の相対参照はすべてそのフォルダ基準で解決されます。.kasc+DATA/をまとめたフォルダを納品すれば、受け取った側はダブルクリックだけで開けます(納品用ローカル化の出力とも整合) - ルートの確認: 設定 > プロジェクトタブの「プロジェクトルート」に現在のプロジェクトの実フォルダが表示されます。外部プロジェクトは登録フォルダ、通常プロジェクトは
projects/<id>/の実パスで、相対参照がどこに解決されるかをその場で確認できます - ファイル名は自由:
kasugai_canvas.kasc以外の名前でも開けます。同じフォルダに複数の .kasc を置けば、それぞれ別プロジェクトとして登録されます - システムとプロジェクトの分離管理: 登録情報は exe 隣の
kasugai_canvas.projects.jsonに永続化され、管理(⚙)→ プロジェクト の一覧に残ります(externalフラグ付き)。プロジェクトをインストール先のprojects/に置く必要はなく、任意のフォルダ・ドライブに配置できます - 表示名: フォルダ内に
project.json({"title": "..."})があればその title、なければ群マニフェストの title、さらに無ければフォルダ名を表示名にします(起動対象も群メンバーも同じ規則) - 共有データ(
../参照・階層スコープ): 相対URLの..は .kasc のあるフォルダの1つ上を指します。プロジェクト群フォルダにprojects.json(複数プロジェクト一覧のマニフェスト)を置くと「この上はワークスペース」の宣言になり、連続するマニフェストの数だけ上に出られます。ProjectsA/ProjectA/のような群構成なら../が群内共有、../../DATA/がワークスペースルートの全群共有 DATA になり、ProjectsA/・ProjectsB/のような複数のプロジェクト群で同じ DATA/ を共有できます。マニフェストが無いフラット構造は従来通り1階層上まで。通常プロジェクトも同じ意味で、../はprojects/直下(../DATA/= 全プロジェクト共有のprojects/DATA/)を指します - プロジェクト群の一括登録: 群の中の .kasc を1つダブルクリック(または登録)すると、親フォルダの
projects.jsonが読まれ、マニフェストに列挙された全プロジェクトがまとめて登録されます。エントリは例として{"dir":"ProjectA","title":"計画A","kasc":"a.kasc"}と記述できます。登録IDは指定不要です。従来のidも受理し、dir省略時は id と同名のサブフォルダを使います。kasc省略時はフォルダ直下の .kasc を辞書順で選ぶため、複数ファイルがある場合は明記してください。無効なフォルダ・明示されたファイルはスキップします。応答のregisteredに登録された全 id が返ります - 登録解除:
DELETE /api/projects/<id>で一覧から外せます(ファイルは削除しません) /api/files・/api/cloud/localize等のプロジェクト系 API も外部プロジェクトに対応します。通常のファイル保存は登録フォルダ内、納品用ローカル化は登録フォルダの隣に<dir>_local/ワークスペースを生成します- ローカルサーバー(local ティア)のみの機能です。静的配信・コンテナ実行では関連付け自体が存在しません
推奨納品パッケージ構造(プロジェクト群+共有DATA)
納品フォルダ/ … ワークスペースルート。ZIP にしてそのまま渡せる
├─ DATA/ … 全プロジェクト群で共有(../../DATA/… で参照)
├─ ProjectsA/
│ ├─ projects.json … 群マニフェスト [{"dir":"ProjectA","title":"計画A","kasc":"xxx.kasc"}, ...]
│ │ (群のプロジェクト一括登録+共有スコープ宣言の役割)
│ ├─ DATA/ … (任意)群内だけの共有(../DATA/… で参照)
│ ├─ ProjectA/
│ │ ├─ xxx.kasc … geojson: 名 | ../../DATA/共有.geojson で全群共有を参照
│ │ ├─ DATA/ … (任意)プロジェクト専用(DATA/… で参照)
│ │ └─ project.json … (任意)表示名 {"title": "..."}
│ └─ ProjectB/
│ └─ yyy.kasc
└─ ProjectsB/
├─ projects.json
└─ ProjectC/
└─ zzz.kasc … ProjectsA と同じ ../../DATA/ を共有できる
DATA/x→ プロジェクトフォルダ内の DATA/(プロジェクト専用)../DATA/x→ 群フォルダ内の DATA/(群内のプロジェクトで共有)../../DATA/x→ ワークスペースルートの DATA/(全プロジェクト群で共有)projects.jsonを持たないフラット構造({P1, DATA})は従来通り../が1つ上を指し、../../は拒否されます
受け取り側は ZIP を展開して群内の .kasc をどれか1つダブルクリックするだけで、マニフェストに従って群の全プロジェクトが登録され、相対参照・共有データのすべてが解決します。