この記事は株式会社ヘンリーAdvent Calendar 2025の20日目の記事です。
Arc Raiders にハマっているエンジニアの立野です。#ドンシュー界隈 です。
クラウドネイティブ……なのにインストールするソフトとは?
Henry は国・厚生労働省が推奨するクラウドネイティブ型の電子カルテ&レセコンです。そうは言っても、実はお客さまの病院やクリニックにひとつだけ必ずインストール、常駐させていただくソフトがあります。
そのソフト「常駐プロセス」はとても大切な役割を担っています。「弊社サーバー」と「お客さま施設内の医療機器、事務機器、オンプレ型ソフトなど」を、双方向に、インターネット越しに、安全につなぐことです。
院内機器やソフトで、インターネットとやりとりする仕組みを備えた IoT 的なものが増えればいいのですが、VPoE 戸田がポッドキャスト第1回でも触れたように、そうしたものはまだまだ少ないのが現状です。インターネットに繋がらない繋げない環境と、クラウド型ソフトを安全に繋ぐ。こうした仲介者的なソフトは、医療業界だけでなく、飲食業向け、製造業向けの SaaS 開発をしているところでも耳にします。
この常駐プロセスを今年、Tauri というフレームワーク+Rust 言語で書き換えたら、わかってみると実にしょうもない問題で時間を溶かしてしまいました。
ハマり 1. たった1行で Mac 配布版がまったく起動しない件
機能もひととおり実装できたので、Mac 版(.app)を配布ビルドし、テスト実行しました。開発ビルド(cargo run)で元気に動きまくっていた UI が、何ひとつ表示されません。Tauri のログファイルすら作られておらず。Mac の検疫(Gatekeeper)も解除済みなのに、一体なぜ……?
思い返すこと3週間前、Tauri に入門してすぐに試した Hello, world アプリは、元気に動いていました。もりもり実装したコードの何かが、なぜか配布版でだけ、悪さしていそうです。
賢明な読者の方には git bisect などで問題ある git コミットを特定する方もおられるでしょう。ですが、配布版ビルドには2分もかかるので、コミットを探索しながらビルドするのはだるいです。
これで「ストリーミングを開始」して、常駐プロセスを起動させてみると、Mac OS 側がとらえた大量のログを見ることができました。
デフォルト 09:04:02.428143+0900 runningboardd 'app' Constructed job description: { count = 23, transaction: 0, voucher = 0x0, contents = "Platform" => : 1 "ProcessType" => { length = 3, contents = "App" } "EnableTransactions" => : false "_ManagedBy" => { length = 22, contents = "com.apple.runningboard" } "CFBundleIdentifier" => { length = 21, contents = "jp.henry.local.app.v2" } "_ResourceCoalition" => { length = 61, contents = "app" } "_DisablePointerAuth" => : true "ThrottleInterval" => : 2147483647 "MachServices" => { count = 0, transaction: 0, voucher = 0x0, contents = }
...(以下複数行に渡り数万文字)
「エラーメッセージをちゃんと読め」はITエンジニアの鉄則としてよく言われます。さすがにこんなに複雑な構造で、まるで無関係さそうな情報だらけのログを何万文字も見るのはしんどいです。生成 AI にログを丸投げすると、ズバリ怪しいところが見つかりました。
"WorkingDirectory" => { length = 1, contents = "/" }
なんと、WorkingDirectory がプロセスを起動したディレクトリではなく、ルート / になっています。Rust の env::current_dir で取得したディレクトリにファイルを書き込む処理が、/ への書込権限なく異常終了……というのが真相でした。これは Tauri や Rust のせいではなく、Mac のアプリバンドル(.app)の既知の問題だそうです。
解決策として env::current_dir をやめて、Tauri の PathResolver で Tauri アプリのローカルデータディレクトリを取得し、そこに書き込むようにしました。
let working_dir = app // AppHandle
.path() // Pathresolver
.resolve("", BaseDirectory::AppLocalData);
ハマり 2. お客様の環境でなぜか Window 版がまったく起動しない件
先ほどの問題も解決し、Mac や Windows での配布版のテストも重ね、とうとうリリースを迎えました。……が、お客さまの環境でまったく起動せず。またもやログすら出ません。Mac のコンソール相当の Windows イベントビューアを見ても、それらしい項目が見当たりません。Kotlin で実装されていた旧バージョンに切り戻して、リリースを取り止めました。
生成 AI の力を借り、プロセス(Tauri アプリとして実装)が Windows で動かない理由として考えられる10個くらいの原因調査するスクリプトを作成しました。日を改めてお客様環境につなぎ、調査スクリプトを走らせたところ、なんと、少しは起動処理が進み、今度はログが出ました。「必須の環境変数 foo が設定されていない」ということが分かりました。
色々と試したところ、
- 一般ユーザーでログインし、常駐プロセスインストールする(ログアウトしない)
- 管理者ユーザーでログインし、アプリに必要なシステム環境変数を設定する(ログアウトしない)
- 一般ユーザーのセッションに戻り、プロセスを起動すると、☝️のシステム環境変数を
env::varで読み込めない
(1回目のリリースはここで諦めた) - 一般ユーザーで、コマンドプロンプトで
SET foo=barなどと一時的に設定した環境変数は、読み込める
- Windows を再起動すると、先ほどのシステム環境変数が、一般ユーザーで起動したプロセスから読めるようになる
2回目のリリースはここ(ただし全部の環境変数を設定しておらず不足があった)
というように「管理者で設定したシステム環境変数が、別の一般ユーザーのログインセッションにすぐに読み込めない」というのが原因でした。対策として「初回インストール時は環境変数を設定したあとに必ず再起動する」という手順にしても良いのですが、忘れてしまうこともありそうです。
もともとこの常駐プロセスは設定を書くところが (1) 環境変数、(2) YAML設定ファイルの2つありました。使っているライブラリに環境変数を見るものがあるのでこうなったと想像しますが、はっきりした使い分けがされずに何年も開発が進むうちに、アプリとして必須の設定が環境変数とYAMLの両方に分かれてしまっていました。これは開発者にも、このアプリをお客様に導入する導入部の方にも、複雑になっていました。
そこで、環境変数の設定は互換性のため残しつつ、設定をすべてYAMLに集約するように変更しました。これなら、アプリ起動時にYAMLが読めていれば、最新の設定が反映できます。
来週のリリースが「3度目の正直」でうまくいくことをサンタクロースに願ってやみません。

r/ProgrammerHumor/comments/cw58z7/it_works_on_my_machine/
おまけ
色々な罠にハマってしまいましたが、常駐プロセスをTauri+Rust に書き換えたことで、安全な自動更新、プロセスの高速化や省メモリ化など、さまざまな恩恵がありました。
今回の Tauri+Rust の開発について、また別のトラブルをLTしましたので、ご興味があればご覧ください

