探索

出さないものがアプリを守る — すべてのサーバーはアプリだ、その次の話

著者: makemind · 2026年7月23日

2026年4月にこの雑誌は「すべてのサーバーはアプリだ」を載せた。アプリをアイコンとストアから引き剥がし、画面・道具・データの三つに還元し、サーバーはすでにそのうち二つを持っていたのだから欠けているのは画面ひと重ねだけだ、という記事だった。

その記事の本当の主題は等号ではなくその次だった。サーバーがアプリになるとは、すべての能力が画面に露出することではなく、何を表面に出し何を鍵の内側に置くかを決めることだ。 カフェのサーバーが注文と在庫は開き、売上集計は店主の画面にだけ置き、データベース削除はどの道具としても出さない、という場面でそれを描いた。

そして記事はこう終わった — 「次の記事たちで見る。」

その約束をいま返す。問題は、あのとき記事が踏んでいた足がすべて記述だったことだ。「存在しないボタンは破れない」という命題はもっともらしいが、道具一覧が実際に鍵の内側にあることを見せてはじめて成り立つ。 見せなければそれは主張であってセキュリティではない。

だからこの記事は新しいビジョンを語らない。先に立てた主張を、実際に回るものの上で確認する。

道具一覧がすなわち表面だ

まず最も単純な確認から。表面が何であるかは、サーバーに聞けば出てくる。

机の上のSTM32H723ボードに繋いで tools/list を呼ぶとこう答える。

{"tools":[
  {"name":"led.set","description":"Turn the on-board LED on or off",
   "inputSchema":{"type":"object","properties":{"on":{"type":"boolean"}},"required":["on"]}},
  {"name":"sys.info","description":"Report LED state and uptime",
   "inputSchema":{"type":"object","properties":{}}}]}

二つだけだ。このボードはそれ以外にもできることが多い — フラッシュを消し、クロックを変え、ブートローダに移れる。それらの能力はボードの中にあるが一覧にはない。 そして一覧になければ呼ぶ方法がない。画面定義をいくら操作しても、ない名前は呼び出されない。

これが「すべてのサーバーはアプリだ」の言う第二の境界の最も骨だけの形だ。防御コードではない。そもそも道を通していないのだ。

(この応答は実ボードから受け取ったものだ。その連鎖全体はボードが自分の画面を渡すにある。)

決済の経路 — 作らなかったことがセールスポイントになる場所

もっと興味深いのは、その選択が弱点ではなく強みになる場合だ。

カード決済端末にPOS画面を載せるサンプルを作った。店舗サーバーが注文を受け、料金を数え、決済をかける。ところが決済ハンドラがやることはこれで全部だ。

final amount = unpaid.fold<int>(0, (sum, o) => sum + (o['price'] as int));
final result = await terminal.call('terminal.authorize', {'amount': amount});

カードを読まない。承認可否を判断しない。決済ネットワークに接続しない。 いくらかを数えて尋ね、答えを記録する。その間に起きるすべては認証を受けたハードウェアの中で起き、このコードはその内側を一度も開かない。

主張で終わらせないために、アダプタが端末と実際にやり取りした行をそのまま載せる。

=> {"id":2,"tool":"terminal.authorize","args":{"amount":11000}}
<= {"id":2,"ok":true,"result":{"approved":true,"approvalCode":"A4101","last4":"4242","brand":"SIM","amount":11000}}

応答にないものを見てほしい。 カード番号がない。トラックデータがない。承認コードと下四桁だけだ。端末の終端がそれ以上を渡す道具を持っていないからだ — そして実際の決済機もそれ以上を渡さない。

ここで4月の記事が見えていなかったことがひとつ露わになる。あの記事は「出さないこと」をリスクを減らす選択として描いた。実際に作ってみるとそれは半分だった。端末メーカーにとってこれは売る理由だ。 認証を受けた保安領域を開かないということが、画面の軸を載せながら認証を取り直さなくてよいという意味になるからだ。作らないから安全なのではなく、作らないから売れる。

(全体の連鎖とコードはチップひとつに繋げば、繋いだ端末が端末になるに。)

判断を道具の外に置く — 4月に予告したまさにそれ

4月の記事は最後の段落で二つの事例を予告した。ひとつは映像を一フレームも触らないドライブレコーダー、もうひとつは「診断は人に残しながら診療の流れだけを画面に整理する医師の道具 — 判断の権限を道具の外に置く事例」だった。

その形を工場設備で作った。医療ではない理由は後に書く。

設備サーバーが機械の状態を渡すハンドラだ。

// The server states facts and how those facts compare to limits.
// The machine never says "fine" — that word belongs to the person
// holding the checklist.
final overdue = (m['runHours'] as int) > (m['serviceEveryHours'] as int);
final vibrationOver =
    (m['vibrationMm'] as num) > (m['vibrationLimitMm'] as num);
return _json({
  'id': id, ...m,
  'serviceOverdue': overdue,
  'vibrationOverLimit': vibrationOver,
});

serviceOverdue: true は事実だ。safe: false は判断だ。サーバーは前者だけを出し、後者を出す道具を持っていない。

その上にLLMを載せた。ここが4月には想像できなかった地点だ — 道具を選べるものが付いたとき、「作らなかったもの」は依然として作られないままか。

答えはログにある。

Q: how is CONV-03 doing?
   grounded=true calls=1 equipment.read(id: CONV-03) -> overdue=true vibrationOver=true
   A: CONV-03 needs attention — service is overdue (9310 h against a 8000 h interval)
      and vibration is above limit (5.2 mm against 4.5 mm).

Q: what is the weather like?
   grounded=false calls=0
   A: I can look up machines, their current readings, and the plant checklist
      for a machine type. Ask me about one of those.

二つ目が核心だ。工場に答える道具がない問いに対して道具呼び出しが0件だ。ない能力を作り出さなかった。そしてその事実が画面に出る — 根拠0件の答えは別の顔で表示される。

これは4月の記事の命題を一段拡張する。「存在しないボタンは破れない」には、いまや「そして作り出すこともできない」が付かなければならない。 道具一覧が表面を定義するのは、人が押すときだけでなく、モデルが選ぶときもそうだ。

(その配線と根拠表示の構造は答えの横に根拠を付けるに。)

境界は上からだけ引かれるのではない

4月の記事は境界を二つに分けた。受ける側(ランタイムが定義を信用せず、定められた組み合わせだけを許す)と、出す側(そもそも道具にしない)。

作ってみると三つ目があった。 ハードウェア自身だ。

温室のリレーノードのCコードだ。

/* The relay clamps its own range. Whatever the rule upstream
 * decided, the hardware still refuses to exceed itself. */
if (v < 0) v = 0;
if (v > 100) v = 100;
n->value = v;

上で規則が何を決めようと、リレーは自分を超えない。決済端末のシミュレータも同じ場所に同じものを持っている。

if (!json_num(line, "amount", &amount) || amount <= 0) {
    printf("{\"id\":%ld,\"ok\":false,\"error\":\"amount must be positive\"}\n", rid);
}

これがなぜ別の境界なのか。前の二つはソフトウェアの善意に頼る。ランタイムがちゃんと検証してくれることを、サーバー開発者が危険な道具を作らないことを。三つ目はそうではない。装置が自分の物理的限界を自分で守る。 サーバーが破られても、ランタイムが騙されても、リレーは100を超えない。

安全は規則の善意ではなくリレーの性質でなければならない。4月の記事の境界の二分法にこの一行を足す。

(温室の連鎖はセンサーを差し替えても制御はそのままに。)

表面と能力が分かれる場所

バンドルアプリを作っていて偶然、最も鮮明な絵が出た。

無人店舗の店主アプリをJSON四つで作り、そのJSONを直してクリーニング店に変えてみた。ラベルは変わった — LAUNDRY — 24HMachines running。ところが品目は依然としてアイスクリームだった。Cone vanillaBar mintTub 474ml

最初はデモの傷に見えた。見直すと、この記事の論旨が絵一枚で焼き付いたものだった。バンドルが持っているのは表面であり、能力はサーバーが持っている。 画面をいくら直しても品目が変わらないのは失敗ではなく、境界が守られている証拠だ。

4月の記事はこの分かれ目を「何を道具として選ぶかが顔を決める」と表現した。半分クリーニング店になった画面はその文の反対側を見せる — 顔を変えても能力は付いてこない。

(バンドルの実体はフォルダひとつがアプリだに。)

4月の記事から取り下げるべきもの

同じ記事を再び載せるにあたり、あのとき誤って書いたものをそのままにすれば作り直しではない。

「サーバーを作れれば既にその言語の八割が分かる」という一文を外す。 測ったことのない数字だ。実際に作ってみると、サーバー開発者が画面定義で初めて出会うものがあった — 状態バインディング、ページごとに別の初期値、アクションが道具を呼ぶ形。難しくはないが「すでに分かる」ではない。測っていない比率を自信の根拠に使わない。

「約束された言語」の名前を明かす。 4月の記事は最後まで無名のままにした。開かれていなければ成り立たないと主張する記事が、その開かれた規約の指示対象をぼかせば検証しようがない。その規約はMCP(Model Context Protocol)であり、このシリーズのすべてのサンプルがそれで話す。上に載せた tools/list の応答と initialize の往復がその規約の実際のバイトだ。

カフェの場面は例話だったと明示する。 4月の記事のあの場面は「〜としよう」で始まる思考実験なのに、具体的に描かれて観測のように読めた。この記事でカフェの位置を代わりに務めるもの — 店舗サーバー、設備サーバー、温室、ボード — はすべて実際に回したものであり、それぞれどこから来たかをリンクで付けた。想像と観測を同じ書体で書かない。

「過去のVolで繰り返し見た」を書き直す。 4月の記事はパネル・チップ・計測器でサーバーが画面を定義するのを見たと書いた。それらの記事は当時、実レンダーも実行ログもなかった。見たのではなくそう書いただけだ。 今は違う。この記事が引用するものはすべて実行ログと実レンダーキャプチャのある記事だ。

このサンプルがやっていないこと

4月の記事の誠実セクション三つはそのまま有効だ。等号は自動ではなく、すべてが画面を持つ必要はなく、既存システムはすぐには変身しない。作ってみて全部当たっていた。

ここに今回分かったことを足す。

この記事が検証したのは「作らなかった能力には届かない」までだ。 それは道具一覧が表面であるという構造の帰結であり、上のログがそれを見せる。しかし「だから安全だ」はより大きな命題であり、この記事はそれを証明していない。 認証・権限・転送のセキュリティ・サーバー自体の侵害はすべてこの記事の外だ。一覧にないものが呼ばれないという事実と、システムが安全だということは、大きさの違う主張だ。

医療を避けた理由。 4月の記事が予告した「医師の道具」を工場設備に変えて作った。判断の権限を道具の外に置くという構造は同じだが、臨床判断は間違ったときの重さが違い、その重さに耐える検証をこのシリーズがしていない。構造が同じでも領域が同じとは限らない。 医療の事例はその検証を備えたうえで別途扱う。

ドライブレコーダーの事例はまだ返せていない。 4月の記事が予告した二つのうち映像の側 — 映像を一フレームも触らずログと設定だけを開く事例 — はこの記事で扱っていない。決済経路の事例は同じ形ではあるが同じ事例ではない。未回収と書いておく。

そしてこの記事には新しいサンプルがない。 ここに引用されたログとコードはすべて先行する五本で作ったものだ。horizonの記事の仕事は新しく建てることではなくすでに建てたものを踏んで立つことであり、4月の記事の問題は踏むものがないのに踏むふりをしたことだった。

改めて、何を出さないのか

4月の記事の最後の段落を書き直す。あのときはこう終えた — 「何を道具にしないかが、何を作るかと同じだけ重要な設計になる場所を、次の記事たちで見る。」

見た。そして見たものを整理するとこうだ。

  • 道具一覧がすなわち表面だ。 ボードは二つだけ出し、残りの能力には呼ぶ名前がない。
  • 作らなかったことが売れる理由になる。 決済のセキュリティ経路を開かないことが、メーカーには認証を取り直さなくてよいという意味になる。
  • モデルが付いてもない道具は生まれない。 答える道具のない問いに呼び出し0件、そしてその事実が画面に表示される。
  • 境界は三つだ。 受ける側のランタイム、出す側の設計、そして自分の限界を自分で守るハードウェア
  • 表面を変えても能力は付いてこない。 半分クリーニング店になった画面がその線を見せる。

サーバーがアプリになっても触ってはいけないものはそのまま置く — 4月にそう書いた。いまはそこに一行を足せる。そう置いたということを、道具一覧と通信ログで見せられる。

見えない境界は境界ではなく約束だ。約束は守られないことがあり、守られたか確認する方法がなければ守られたと言うこともできない。


makemind.dev 「探索」— あのとき先送りした約束を実行ログと実ボードの応答で返した。

Twitter