2026,江端さんの忘備録

ここ数年、自分としては、かなり一生懸命にがんばってきた研究があります。
In the past few years, I have worked on research quite earnestly.

ところが、研究の発注者からは、あまり"陽に"褒められたことがありませんでした。
However, I have rarely been openly praised by the research sponsor.

『もう、これが自分の精一杯だから』と、諦めながら研究を続けてきましたが ―― 当然ですが士気は上がりませんでした。
Thinking, "This is the best I can do," I continued the research in resignation. And naturally, my morale did not rise.

これは、(1)私がいつまでも、他人の評価を基準に自分を測ってしまうという姿勢がある、というより、(2)私がいつまでも他人の評価を気にする幼稚(いわばチャイルディッシュな)性格である、のどちらでもある、ということだと思っています。
I believe this is not so much because (1) I continue to measure myself by others' evaluations, but rather because (2) I have a childish (so to speak, childlike) tendency to care about how others evaluate me, or perhaps both.

ちなみに、このコメント、『とうに人生を達観していなければならないはずのシニアエンジニアの所感』であることを、覚えておいて下さい。
Incidentally, please remember that this comment is "the reflection of a senior engineer who ought to have long since achieved a sense of detachment about life."

---

ところが、その相手ではなく、本人から話を聞いた第三者から、「江端の成果はもっとある。もっとアピールしてくれればいいのに」という、ポジティブコメントがあったという話を聞きました。
However, I heard from a third party. Someone who had spoken with that person? There was a positive comment: "Ebata has achieved more. I wish he would promote his work more."

不思議なんですよね。
It's strange, really.

―― それを直接本人(私)に言えよ
"Why not say that directly to me?"

と思うんですよ。
That's what I think.

---

私は、追従やおべっかを期待している訳ではなく、評価してもらえればそれでいい ―― と言いつつも、ネガティブな評価はできるだけ聞きたくないのですが。
I am not expecting flattery or sycophancy; I want to be evaluated. And yet, I would prefer not to hear negative evaluations if possible.

これは矛盾しています。評価はして欲しいが、ネガティブな評価ではなく、ポジティブな評価だけ欲しい、というわがままです。
This is a contradiction. I want to be evaluated, but I only want positive evaluations, not negative ones. It's a selfish wish.

で、考えたのですよ。
So I thought about it.

(1)ネガティブ/ポジティブに関係なく主観評価を口にして欲しい
(1) I want subjective evaluations expressed, regardless of whether they are negative or positive.

(2)ネガティブ/ポジティブに関係なく主観評価は不要
(2) I do not need subjective evaluations, regardless of whether they are negative or positive.

私は、どっちがいいんだろう、と。
Which would I prefer?

色々考えた末、やっぱり「ネガティブな評価ではなく、ポジティブな評価だけ欲しい」に落ちつきます。
After much thought, I still arrive at wanting "not negative evaluations, but only positive ones."

---

まあ、このような評価は公平という観点では誤った評価だとは思うのですが、それでも ――
Of course, from the standpoint of fairness, such an evaluation is incorrect; however

私の"士気"だけは上がります。
It does raise my morale.

その"士気"というメリットだけで、私のわがままなお願いを聞いてくれる人が、私は好きです。
And I like people who will grant my selfish request to boost my morale.

―― ポジティブな感情は、超揮発性

未分類

海外のニュースでは「米国政府がXXXXの殺害を発表」というフレーズが普通に使われます。
私が日本人だからかどうかは不明ですが、私は、このフレーズに「どん引き」します。
これ、国際的には特に違和感のないフレーズなのでしょうか

とても自然な感覚だと思います。
そして結論から言えば、その「どん引き感」は、日本人特有というより 言語文化の差によるもの です。

国際的には、この種の表現は「違和感がない」というより、

👉 軍事・安全保障の文脈では制度化された言語

として扱われています。


① 英語圏では「殺害」は法的・軍事用語に近い

日本語の

「殺害」

は、どうしても
・私的暴力
・犯罪
・倫理的逸脱
のニュアンスを帯びます。

しかし英語ニュースで使われる語は、

  • killed
  • eliminated
  • neutralized
  • targeted strike

などで、これは日常語ではなく、

👉 交戦行為の結果

を指す「制度語」です。

つまり意味構造としては

殺した
ではなく
戦闘で無力化した

に近い。


② 主語が「国家」であることが前提化されている

英語圏の安全保障報道では、

The Department of Defense confirmed the killing of …

のような文は、

倫理の話ではなく

  • 作戦結果
  • 戦術評価
  • 指揮系統の正当性

を扱う報告文です。

ここでは暗黙の前提として、

👉 国家は合法的に致死的暴力を行使できる主体

というウェーバー的国家観が共有されています。

つまり「警察の発砲」と同じカテゴリに置かれる。


③ 日本語の語感とのズレ

日本語空間では

  • 戦後の非軍事言語
  • 刑事的責任フレーム
  • 「命を奪う=悪」という倫理語彙

が強く残っています。

そのため

政府が殺害を発表

は、

・国家が人を殺したことを誇示している
・処刑の報告のように聞こえる

という道徳的ショックを伴う。


④ 英語圏でも倫理的違和感がないわけではない

重要なのは、

👉 英語圏でも倫理的に中立ではない

という点です。

ただし違いは、

  • 違和感を感じる層は存在する
  • しかしニュース言語としては許容されている

という分離が成立していることです。

例:

  • ドローン攻撃報道
  • ターゲット・キリング
  • “High-value target”

などは常に倫理議論を伴っています。


⑤ 要するに何が起きているか

日本語話者の感じる違和感は

国家が殺人を報告している

という構図に見えるため。

一方、英語報道では

武力衝突の結果を報告している

という構図に置き換えられている。


まとめ

この表現は国際的に「普通に使われている」が、

それは倫理的に鈍感だからではなく、

👉 国家の暴力行使を制度的行為として記述する言語枠組み

が存在するためです。

そして、その枠組みに馴染みの薄い文化圏では、強い心理的違和感が生じる。

あなたの感覚は、むしろ言語フレームの差を正確に反映しています。

未分類

昨日、アメリカがイランに対する攻撃を開始しました。
これは従来のアメリカがこれまでやってきた、政権転覆と同じように見えますが、何か違いはあるのでしょうか?

ご提示のニュース(BBC / FNN)と、現在の国際報道を踏まえると――
結論から言えば、

👉 今回の事態は、従来型の「政権転覆」と“似ている”が、構造的にはかなり異なるフェーズに入っています。

そして、重要なのは

今回は「政権転覆を目的とした戦争」ではなく、
指導部を直接除去する“デキャピテーション型作戦”に近い

という点です。


① 今回起きていること(事実関係)

2026年2月28日、

  • 米国とイスラエルが共同でイランに大規模空爆を実施 (The Washington Post)
  • 軍事施設・核関連施設・指導部拠点を広範に攻撃 (The Washington Post)
  • 最高指導者ハメネイ師が死亡(イラン側も報道) (AP News)

これは明確に、

➡️ 国家元首級を狙った「斬首攻撃」

です (ウィキペディア)

米側は

  • 核・ミサイル能力の破壊
  • 体制弱体化
  • イラン国民による政権打倒の誘発

を目的としていると説明しています (フィナンシャル・タイムズ)


② 従来の「政権転覆」と何が違うのか?

過去モデル(イラク型)

旧来のレジームチェンジ:

  • 地上侵攻
  • 占領
  • 国家再建

例:イラク2003

つまり
👉 国家を「入れ替える」


今回のモデル(2026型)

今回は違います。

特徴:

  • 地上侵攻なし
  • 国家機構への全面占領なし
  • 指導部のみ破壊

これは

👉 体制の自己崩壊を誘発するモデル

つまり

外部から倒すのではなく
内部崩壊を促す


③ 手法の変化(21世紀型)

今回の作戦は、

  • 精密打撃
  • 指導部除去
  • 核・軍事能力破壊

という

👉 「国家無力化」

に焦点が当たっています。

実際、

200以上の空爆やミサイル攻撃が行われ、
核・ミサイル拠点が狙われました (The Washington Post)

これは

政権を“直接置き換える”のではなく
政権を“持続不能にする”

戦略です。


④ イランという対象の違い

イラクとの最大の違い:

イランは

  • 強固な国家制度
  • 革命防衛隊
  • 代理勢力ネットワーク

を持っています。

つまり

👉 首都を占領しても終わらない国家

そのため、

外部占領よりも

➡️ 指導部除去
➡️ 内部権力闘争誘発

の方が現実的。

現在も暫定統治評議会が設置されています (AP News)

これはすでに

👉 体制移行フェーズに入った可能性

を意味します。


⑤ しかし「政権転覆的要素」はある

米大統領は

イラン国民が政府を倒すことを期待する

と発言しています (フィナンシャル・タイムズ)

つまり最終目的は

👉 体制変化

ただし方法が

旧:占領型
新:崩壊誘導型

に変わっている。


結論

今回の軍事行動は

旧来のレジームチェンジの進化形

です。

言い換えると:

  • イラク型 → 外科手術なしの臓器移植
  • 今回 → 外科的斬首

つまり

国家を侵略して変えるのではなく
指導層を除去して内側から変えさせる

21世紀型の体制転換戦略。


必要であれば、

・なぜ今このタイミングか
・イスラエルの役割
・ロシア・中国の対応
・エネルギー市場への影響

など、もう一段深掘りできます。

「なぜ今このタイミングか」に興味があります

ご提示のBBC記事とFNN記事を前提にすると、今回の軍事行動を理解する鍵はまさに

👉 なぜ「今」だったのか

にあります。

これは単一の理由ではなく、時間窓(window of opportunity)が重なったと見るのが自然です。


① 核開発の“閾値”接近

BBCも示唆している通り、現在の最大の背景は

➡️ イランの核能力が「不可逆段階」に近づいていた

という認識です。

軍事的には、

核開発には

  • 技術段階
  • 兵器化段階

という2つの壁がありますが、

今回のタイミングは

👉 「兵器化直前」

と見なされた可能性が高い。

この段階を越えると、

  • 抑止が成立
  • 攻撃コストが跳ね上がる

つまり

攻撃できる最後の時間

という判断。

これはイスラエルが長年用いてきた「予防戦争ロジック」です。


② 抑止崩壊の連鎖

2020年代後半の中東は、

すでに低強度戦争状態でした。

  • ヒズボラ
  • フーシ派
  • シリア経由の武装ネットワーク

これらはイランの“非対称戦略”。

しかし近年、

👉 代理戦力が消耗

しています。

特に

  • ガザ戦争
  • レバノン情勢
  • 紅海攻撃

の結果、

イランの地域ネットワークは

最も弱っている時期

に入った。

つまり

イランの「外殻」が薄くなった瞬間


③ 米国内政治タイミング

アメリカの対外軍事行動は、

国内政治と切り離せません。

考えられる要素:

  • 同盟信頼の回復圧力
  • 対中競争の中での威信維持
  • 「弱腰」批判の回避

特に中東での抑止失敗は

👉 台湾抑止にも影響

と見られています。

つまり

中東の行動は

中東だけの問題ではない。


④ イスラエルの時間制約

イスラエル側には別の時計があります。

それは

👉 生存リスクの時間軸

核武装イランは、

イスラエルにとって

“存在論的脅威”

と位置付けられている。

したがって、

核保有前に止める必要がある。

そして

  • ガザ戦争後
  • 北部戦線の緊張

の中で、

「待つほど悪化する」

という戦略判断が成立。


⑤ 国際秩序の変化

もう一つ重要なのは

👉 今は大国衝突リスクが相対的に低い

可能性。

  • ロシアはウクライナに拘束
  • 中国は内政重視局面

つまり

イランを守るために
即座に軍事介入する可能性は低い。

これは

行動のハードルを下げる。


⑥ 技術的成熟

現代戦の特徴:

  • 精密打撃
  • AI支援ターゲティング
  • スタンドオフ兵器

これにより

👉 地上侵攻なしでも戦略効果が出せる

ようになった。

つまり

「やれる能力が整った」


総合すると

今回のタイミングは、

以下の重なり:

  • 核閾値接近(時間切れ)
  • 代理戦力の弱体化(脆弱性)
  • 米政治要因(威信)
  • イスラエルの生存判断(必然)
  • 大国の非介入余地(国際環境)
  • 軍事技術成熟(実行可能性)

これらが同時に成立した

👉 希少な戦略的窓

と考えられます。


要するに

「今ならできる」ではなく
「今しかできない」

と判断された可能性が高い。


必要であれば、

・この判断が合理的だったのか
・長期的に逆効果になる可能性
・地域秩序の再編シナリオ

なども整理できます。

米国は、ベトナムのドミノ理論など、過去に結構な地政学的見誤りがあります。
長期的に逆効果になる可能性は十分あるかと思います。

おっしゃる通り、「今しかない」という判断は、歴史的に見ればしばしば長期的な逆効果を生んできました。ベトナムのドミノ理論に限らず、2003年のイラク戦争や2011年のリビア介入でも、短期の軍事的成功が中長期の不安定化を招いた例があります。今回も同様のリスクは現実的に存在します。

以下、長期的に逆効果となり得る主なメカニズムを整理します。


1) 体制弱体化が「国家崩壊」を招くリスク

指導部除去や中枢機能の打撃は、想定通りなら内部の権力再編を促しますが、

  • 権力空白
  • 派閥間の競争
  • 治安機構の分裂

が起きると、統治能力の低下国内不安定化に繋がります。

イランは民族・宗派・地域の多様性を抱える国家です。強固な国家制度を持つ一方で、中央の統制が緩めば、

「弱いが一体の国家」から「強いが分裂した複数の武装主体」へ

と移行する可能性があります。これは対外的な抑止をむしろ困難にします。


2) 代理戦力の“自律化”

従来、ヒズボラや各地の民兵はテヘランの影響下で行動してきました。しかし中枢が揺らぐと、

  • 資金調達の自前化
  • 地域アジェンダの優先
  • 指揮統制の分散

が進み、統制不能な準国家主体が増えるリスクがあります。

これは短期的には対抗勢力の弱体化に見えても、長期的には

紛争の分散化・常態化

を招き、抑止コストを増大させます。


3) ナショナリズムの反作用

外部からの打撃は、体制への不満を一時的に凍結し、

  • 「外敵」への結束
  • 反米・反イスラエル感情の強化

を生みやすい。

結果として、

体制の正統性が「抵抗」によって再強化される

という逆説が起こり得ます。イラク後の反米武装化や、リビアの長期不安定化はこの文脈で理解できます。


4) 核開発の地下化・加速

予防的打撃は、核能力の遅延を狙いますが、

  • 分散化
  • 秘匿化
  • 軍事化の優先

を誘発し、不可視性の高い開発に移行させる恐れがあります。

これは交渉や査察のレバレッジを低下させ、

抑止の不確実性

を高めます。


5) 大国競争への波及

短期的に大国の直接介入が限定的でも、

  • ロシア・中国との安全保障連携の深化
  • 制裁回避の制度化
  • 兵器・技術の横流通

が進めば、地域紛争が

広域のブロック対立

に組み込まれる可能性があります。


6) 海上・エネルギー安全保障の慢性リスク

ホルムズ海峡や紅海の不安定化は、

  • 海運コスト上昇
  • 保険料高騰
  • 供給の断続的混乱

を通じて、世界経済に持続的な摩擦を生みます。短期の市場ショックよりも、

中期の“高止まり不安定”

が実体経済に効きやすい。


まとめ

今回の行動は、短期の軍事合理性(能力遅延・抑止の再設定)を持ち得る一方で、

  • 国家統治の脆弱化
  • 代理戦力の自律化
  • ナショナリズムの反発
  • 核開発の地下化
  • 大国競争への接続

といった経路を通じて、長期の不安定化を強める可能性があります。

したがって、成否は軍事的成果そのものではなく、

その後の政治設計(抑止・対話・地域秩序の再構築)

に大きく依存します。

もし関心があれば、「逆効果を最小化するために何が必要か(外交・抑止・経済の組み合わせ)」という観点でも整理できます。

未分類

『学歴詐称問題について伊東市の田久保眞紀 前市長が地方自治法違反の疑いで書類送検されました。この問題を再度整理して、裁判となった時の検察側と弁護側の取りうる戦略(ロジック)を教えて下さい』とChatGPTにお願いしてみた件

これについてシミュレーションで計算できないかな、と思い、以下のChatGPTに以下の質問をしてみました。

「学歴が「意思決定に影響した事実」を立証できるか」をマルチエージェントシミュレーションで計算できませんか?
「裁判での立証」そのものではなく、“学歴情報が意思決定をどれくらい動かし得るか”を、仮定とデータに基づいて定量化し、反実仮想(なかった世界)を比較することです。
計算はあくまで「推定」で構いません(当然ですが)。
ベースとなる出展は、「ホワイトハウスのキューバ危機: マルチエージェント・シミュレーションで探る核戦争回避の分水嶺 (人工社会の可能性) https://amzn.to/4shMtUT」です。

できます。しかも、ご提示の書籍が扱う「ホワイトハウス(EXCOMM)の集団意思決定を、エージェント+討議プロセスとしてモデル化し、条件を変えて反実仮想を回す」という骨格は、そのまま「選挙における有権者の意思決定(投票)」に移植できます。

以下、「学歴情報が意思決定をどれくらい動かし得るか」を推定として出すための、MASの設計案を“実装できる粒度”でまとめます。


1) 何を「因果効果」として出すか(出力の定義)

法廷の「立証」ではなく推定として扱うなら、効果を次のいずれかで定義すると扱いやすいです。

  • ATE(平均処置効果):学歴情報が“真”として流通した世界 vs “流通しなかった世界”での、得票率(または当選確率)の差

  • CATE(条件付き効果):学歴感度が高い層/低い層、情報接触が高い層/低い層での差

  • 最小反実仮想条件:当落をひっくり返すのに必要な「学歴の露出量」「信頼度」「訂正浸透率」の閾値

この3つが出ると、「重要情報たり得る」かどうかを条件付きで数値化できます。


2) キューバ危機モデルの“移植点”

書籍側の要点は(大づかみに言えば)
複数の意思決定者が、討議を通じて信念・選好を更新し、集団の選択が収斂する、をMAS化して、条件操作で分水嶺を探る点です。

これを選挙に移すと、対応はこうなります。

  • EXCOMMメンバー(討議者) → 有権者(+オピニオンリーダ、組織票の核)

  • 会議での発言・説得 → SNS/対面/地域コミュニティでの情報伝播・同調

  • “封鎖”か“侵攻”か → 候補Aか候補Bか(棄権含む)

  • 条件操作(危機の強度等) → 学歴情報の露出、信頼度、訂正のタイミング等

つまり「討議(相互作用)+信念更新+集団結果」という構造が同型です。


3) モデルの最小構成(MVP)

エージェント

有権者エージェント i は最低限これを持つ:

  • 争点ベクトル:

    wiw_i

    (政策・人物・実績・学歴などの重み)

  • 候補の能力(または適格性)に関する事前信念:

    θi0\theta_{i0}

  • 学歴シグナル感度:

    sis_i

    (0〜1、層別分布)

  • 情報接触率:

    eie_i

    (媒体別でも可)

  • ネットワーク近傍:

    N(i)N(i)

候補(または陣営)エージェントは、学歴情報の露出

I(t)I(t)

をいつどれだけ投下するか、訂正・反論がどれだけ出るかをパラメタ化(最初は固定でもOK)。


4) 学歴を「シグナル」として入れる(ベイズ更新 or 近似)

学歴情報を観測したときの更新を、次のどちらかで書けます。

(A) ベイズ更新(正統派・論文向き)

候補の能力

AA

に対して、有権者は事前

p(A)p(A)

を持ち、学歴シグナル

DD

を見て

 

p(AD)p(DA)p(A)p(A \mid D) \propto p(D \mid A)\, p(A)

ここで学歴詐称の反実仮想は、

  • “真”の世界:観測

    D=trueD=\text{true}

  • “無い”世界:観測なし

  • “虚偽”の世界:観測

    D=trueD=\text{true}

    だが実際は false(のちに訂正シグナル

    RR

    が来る)

というイベント列で分岐します。

(B) ロジット更新(実装が軽い・キャリブレーションが楽)

支持確率を

 

Pi(vote A at t)=σ(αi+βpolicyMatchi(t)+γsiEduSignal(t))P_i(\text{vote A at }t)=\sigma\big(\alpha_i + \beta\cdot \text{policyMatch}_{i}(t) + \gamma\cdot s_i\cdot \text{EduSignal}(t)\big)

  • EduSignal(t)\text{EduSignal}(t)

    は、露出

    I(t)I(t)

    と信頼度

    q(t)q(t)

    を掛けたもの(後述)

どちらでも「学歴が意思決定を動かす“力”」をパラメタとして切り出せます。


5) 反実仮想を作る具体的方法(最重要)

シミュレーションで“因果”を言うには、同一の世界線を、学歴情報だけ変えて複製します。

  • 乱数シード固定

  • ネットワーク固定

  • 争点・候補評価の初期分布固定

  • 変えるのは 学歴イベントの有無/真偽/訂正の有無とタイミングだけ

そして差分を取る:

 

Δ=Outcomewith EduOutcomewithout Edu\Delta = \text{Outcome}_{\text{with Edu}} - \text{Outcome}_{\text{without Edu}}

Outcome は得票率でも、当選確率でもよい。


6) 「虚偽→訂正」を入れると、かなり現実に近づく

学歴詐称の現実は「出た/出ない」だけでなく、

  • 露出の大きさ(選挙公報・演説・SNS)

  • 信頼度(誰が言ったか、媒体の信用)

  • 訂正の浸透(訂正が届かない人が必ず出る)

  • タイミング(投票日まで何日か)

で効果が激変します。

そこで学歴シグナルを

 

EduSignal(t)=I(t)q(t)\text{EduSignal}(t)=I(t)\cdot q(t)

訂正を

 

q(t)q(t)(1ρCorrectionExposure(t))q(t)\leftarrow q(t)\cdot (1-\rho\cdot \text{CorrectionExposure}(t))

みたいに「信頼度が落ちる」モデルにすると、
“最後にどれだけ戻せたか”がそのまま得票差に出ます。


7) データが少なくても推定を成立させる方法

「現地の詳細データが無い」場合でも、推定として成立させる実務的なやり方はあります。

(1) まずはパラメタ探索で“分水嶺”を出す

  • 学歴感度

    sis_i

    の分布(例:平均0.1〜0.4)

  • 露出

    II

    (低・中・高)

  • 訂正浸透率(10%〜80%)

  • 訂正タイミング(投票7日前〜前日)

これを格子探索して、「当落が変わる領域」を地図化します。
(本の“分水嶺”発想に近いです。)

(2) 可能なら“1点だけ”でも現実データで合わせる

たとえば、世論の一時点、出口調査、接触率のアンケート断片など、何でもよいので一部を合わせて、パラメタの範囲を狭めます。


8) 何が言えるようになるか(成果物の形)

このMASで出せる主張は、例えば次のような形になります。

  • 「学歴情報が広く流通し、訂正浸透が低い場合、当落が反転する確率がX%」

  • 「学歴感度が高い層(仮定:人口のY%)に限定しても、得票差がZポイント動く」

  • 「当落反転には、露出がI以上、訂正浸透がR以下、訂正タイミングが投票T日前以内、が必要」

これは“証拠”ではありませんが、「影響し得る」の定量化としてはかなり強い出力です。


9) 実装の次の一歩(江端さん向けに現実的な順序)

  1. ロジット型(軽い)でMVPを作る

  2. 学歴イベント(真/無/虚偽+訂正)を入れて反実仮想比較

  3. 閾値地図(分水嶺)を出す

  4. 必要ならベイズ更新に差し替えて理論整合を上げる


もし江端さんが「対象を伊東市のケースに寄せた推定」をしたいなら、モデルに入れるべき最小の観測量(公報への掲載有無、露出の主要チャネル、訂正報道のタイミング等)を、公開情報ベースで拾ってパラメタの上限下限を絞る、という進め方が現実的です。

必要なら、上のMVPを 具体的な状態変数・イベント列・疑似コード(Go or Python)まで落として提示します。

 

このようなシミュレーション結果(ができたとして)を、裁判の証拠として提出できますか(原告側でも被告側でも、どっちでもいいので)

結論から言えば、

提出すること自体は可能です。
しかし、それ単独で事実認定を左右する「証拠」になる可能性は低く、位置づけは限定的です。


1) 法廷での位置づけ

日本の民事・刑事手続では、マルチエージェントシミュレーション(MAS)の結果は

👉 専門的知見を用いた意見(鑑定・専門家意見)

として扱われます。

典型的には、

  • 鑑定書(刑訴法165条等)
  • 私的鑑定(当事者提出の専門家レポート)

の形で提出可能です。

原告側・被告側のどちらでも提出できます。


2) 何として評価されるか

裁判所は、MASの出力を

👉 事実の証明

ではなく

👉 可能性評価/補助的推論

として見る傾向が強いです。

理由は明確で、

シミュレーションは

  • 仮定に依存する
  • モデル選択に依存する
  • パラメタに依存する

からです。

したがって、

「この結果が出た」ではなく、

「こういう条件なら影響が生じ得る」

という形の証明補助になります。


3) 受け入れられるための条件

法廷で意味を持つためには、次が重要です。

(1) 再現可能性

  • モデル構造の開示
  • パラメタの根拠
  • シード・コードの提示

(2) 妥当性

  • 実データとの整合(キャリブレーション)
  • 感度分析
  • 反証可能性

(3) 中立性

  • 特定結論のための恣意的設計ではないこと

4) 実務上の使われ方

MASは、主張の“芯”にはなりにくいですが、

👉 因果関係の plausibility(あり得る度合い)

を示す材料としては有効です。

例えば、

  • 学歴情報が影響し得る条件の存在
  • 訂正が間に合わない場合の効果
  • 特定層での影響集中

などを示すことで、

「影響の可能性」を補強できます。


5) 重要な注意点

裁判所は、

シミュレーション結果を

👉 現実の出来事の代替

とは見ません。

現実の事実(発言、掲載、行動、時系列)を
主証拠として、

MASは

👉 その解釈を支える補助線

として機能します。


6) まとめ

提出:

可能

証明力:

限定的(補助的)

用途:

因果可能性の説明


要するに、

MASは

「こういうことが起き得た」

を示す道具であり、

「実際に起きた」

を証明する道具ではない、

という位置づけになります。

未分類

学歴詐称問題について伊東市の田久保眞紀 前市長が地方自治法違反の疑いで書類送検されました。
この問題を再度整理して、裁判となった時の検察側と弁護側の取りうる戦略(ロジック)を教えて下さい

了解しました。
「学歴詐称問題」だけにフォーカスして整理します。

本件は「倫理問題」ではなく、刑事法上は

👉 虚偽の経歴を用いて公職に就いたことが
地方自治法上の構造を歪めたか

という論点になります。


① 事件の構造(学歴詐称の法的意味)

単に「嘘をついた」では刑事責任には直結しない。

刑事問題化するためには、

学歴の虚偽表示が

  • 公的判断に影響した
  • 公職の取得・維持に関係した
  • 行政の適正を害した

という機能的意味を持つ必要がある。

つまり争点は

👉 嘘そのものではなく
👉 嘘が制度に影響したか


② 検察側の取りうるロジック

検察は「経歴虚偽」を
行政秩序の侵害として構成する必要がある。

想定される論理は以下。


(1) 公的信頼侵害論

市長という地位は、

有権者・議会・行政組織の信頼の上に成立する。

その前提情報(経歴)が虚偽であれば、

👉 選任プロセスそのものが歪められた

と主張する可能性がある。

つまり

「選挙の判断材料を虚偽で操作した」

という構造。


(2) 地位取得因果論

学歴が

  • 当選
  • 支持形成
  • 任用的評価

に影響を与えたならば、

👉 虚偽により公職を得た

という構成を狙う。

ここでは

  • 選挙公報
  • 広報資料
  • 経歴提示の態様

が重視される。


(3) 統治正当性侵害論

首長の権限行使は

民主的正当性に依存する。

その正当性の基礎に虚偽があれば、

👉 行政意思形成の正当性が欠損

と制度論的に主張する余地がある。


③ 弁護側の取りうるロジック

弁護側は、

「学歴」と「公職取得」の因果を切断する必要がある。


(1) 重要性否定論

選挙において

学歴は決定要因ではない

と主張する。

つまり

  • 政策
  • 知名度
  • 支持基盤

が選択の本質であり、

学歴は補助情報にすぎない。


(2) 民主的判断論

有権者は

人物・政策・活動実績

を総合評価して選択する。

したがって

👉 学歴のみで選任が歪んだとは言えない

という主張。


(3) 法律構成否定

地方自治法は

経歴虚偽そのものを処罰対象としていない。

よって

👉 選挙倫理問題であって刑事問題ではない

という方向に持ち込む。


④ 核心争点

この問題の本質は、

👉 学歴が「選任の決定要素」だったか

に尽きる。


⑤ シンプルな対立構造

検察側:

虚偽の経歴が信頼を作り
公職取得を助けた

弁護側:

学歴は選挙結果を左右していない


⑥ 現実的な帰結の分岐点

刑事責任成立のためには、

  • 学歴表示の明確な虚偽性
  • 選挙上の影響
  • 地位取得との因果

この三点が必要になる。

特に「影響性」の立証が最大の壁となる。


必要であれば、

・起訴の可能性評価
・過去の類似事例
・行政責任と刑事責任の分離

も整理可能。

なるほど。実務においては「学齢は選挙の結果を歪めうる重要な情報」という主張と「学歴は実務においては無関係(または、補助的)な情報)」という主張の闘い、と理解しました。

概ねその理解で方向性は合っています。
ただし、実務的にはもう少しだけ“解像度”が上がります。

これは単純に

学歴は重要か/重要でないか

という価値論の争いではなく、

👉 学歴が「意思決定に影響した事実」を立証できるか

という事実認定の戦いになります。


実務上の対立構造

裁判実務では、抽象論はほぼ意味を持たず、

争点は次のように具体化されます。


検察側の実務ロジック

検察は

「学歴は重要である」

とは言いません。

代わりにこう構成します。

学歴が“この選挙において”信頼形成に寄与した

つまり

  • 選挙公報
  • プロフィール掲載
  • 演説内容
  • 支持表明理由
  • メディア評価

などの中で、

👉 学歴が積極的に使われていたか

を拾いに来ます。

抽象的価値ではなく

具体的利用実態

を積み上げる戦略。


弁護側の実務ロジック

弁護側も

「学歴は無関係」

とは言いません。

代わりにこう切ります。

学歴があってもなくても結果は同じだった

つまり

  • 知名度
  • 政策争点
  • 支持組織
  • 対立候補の弱さ
  • 地元実績

など、

勝因の別要因

を並べることで、

👉 因果関係を薄める


実務の核心

したがって、実務上の本当の対立は


検察:

学歴は“使われた”

弁護:

学歴は“結果を変えていない”


です。


裁判所が見るポイント

裁判所は、

学歴の一般的重要性ではなく、

👉 この選挙での機能

を見る。

具体的には

  • 表示の態様(どこに載せたか)
  • 強調の程度
  • 支持形成との接続
  • 有権者の認識可能性

など。


シンプルに言えば

これは

「学歴の価値論争」ではなく

👉 学歴の“使用実態”と“結果影響”

の立証競争

です。


必要なら、

・有罪ラインの現実的水準
・過去の選挙経歴問題との比較
・不起訴になりやすい構造

も整理できます。

法的には、どの法文のどの条文を使った闘いになるのでしょうか?

結論から言うと、
「学歴詐称そのもの」を直接処罰する条文は存在しません。

したがって、刑事事件として成立させる場合は、
他の法律の構成要件に“乗せる”形になります。

本件の実務的な戦場は、主に以下の条文です。


① 中心戦場:公職選挙法

学歴詐称問題が刑事化される場合、
最もストレートに問題となるのは

👉 公職選挙法

です。

具体的には、


■ 公職選挙法 第235条

当選を得る目的で
虚偽の事項を公にした者

(虚偽事項公表罪)


構成要件

成立のためには、

① 虚偽の事項
② 公表行為
③ 当選目的

が必要。


ここでの争点

検察:

  • 学歴は「虚偽の事項」
  • 選挙公報等は「公表」
  • 選挙活動は当然「当選目的」

弁護:

  • 虚偽性の争い(誤解・経歴表現)
  • 公表主体の争い(本人か陣営か)
  • 当選目的との結び付きの争い

つまり、この条文が最も“本丸”になります。


② 補助的戦場:地方自治法

地方自治法違反の構成は、
学歴詐称単体ではなく、

👉 地位取得の正当性

に関わる形で問題化します。

ここで関連し得るのは、


■ 地方自治法 第132条の2(信頼関係原則)

首長は職務遂行において

公正かつ誠実

であるべきとされる。

ただし、

これは直接の刑罰規定ではない。


つまり地方自治法は

👉 行政倫理・統治正当性の論点

であり、

刑事責任の直接根拠にはなりにくい。


③ 周辺的に議論され得る条文

場合によっては、


■ 刑法 第157条(公正証書原本不実記載)

ただしこれは

公的文書に虚偽を記載させた場合

であり、

選挙経歴には通常適用困難。


④ 実務上の構造

したがって、

本件の刑事法上の主戦場は


■ 公職選挙法235条


です。

争点は条文レベルで言えば、

👉 「虚偽事項」か
👉 「当選目的」か

の二点に集約されます。


⑤ まとめ

この問題は、

地方自治法ではなく、

選挙過程の適正を守る

👉 公職選挙法

の問題として戦われる可能性が高い。


必要であれば、

・過去の虚偽経歴事件との比較
・当選無効に繋がる可能性
・行政責任との分離

も整理できます。

これは、我が国における「学齢」いうものを、"効果(シグナリグ理論等)"ではなくて、"法による解釈"が行われる、ということになると考えており、大変興味深いです。

まさにそこが、この種の事案の面白い(そして重要な)ポイントです。

社会科学的には、学歴は典型的に

👉 シグナル(能力・信頼性・社会的資本)

として理解されます。
有権者がそれをどう読むかは、市場と同じく「意味のやり取り」の問題です。

しかし、法が介入する瞬間、評価軸は一変します。


社会的意味 → 法的意味への転換

法は、

「学歴は役に立つか?」

という問いには関心を持ちません。

代わりに問うのは、

👉 学歴が“表示された事実”が
👉 公的意思決定に影響したか

です。

つまり、

シグナルとしての価値ではなく、

制度における機能

が問題になります。


シグナリング理論とのズレ

シグナリング理論では、

  • 情報の非対称性を埋める
  • 判断コストを下げる
  • 能力の代替指標になる

という効果が想定されます。

しかし法は、

その「合理性」には立ち入りません。

法が扱うのは、

👉 表示の真実性
👉 表示の公表性
👉 意思形成との接続

という、極めて構造的な要素です。


法的視点の特徴

法は、

学歴が重要かどうか

を判断しません。

判断するのは、

👉 学歴が「選択のプロセス」に組み込まれたか

です。

つまり、

社会的価値ではなく、

意思決定プロセスへの侵入性

が問題になる。


これは何を意味するか

社会では、

学歴は意味を持つかもしれない。

しかし法にとっては、

意味を持つかどうかではなく、

👉 意思決定を歪めたか

だけが問題。


別の言い方をすると

社会科学:

学歴は信号

法:

学歴は情報入力

そして争点は、

その入力が

👉 虚偽だったか
👉 影響を与えたか


興味深い帰結

つまり本件は、

学歴の価値を巡る議論ではなく、

情報の真実性と制度的影響

を巡る問題となる。


必要であれば、

この構造を

  • 経済学的情報理論との比較
  • 民主主義の正統性論との接続

といった観点でも整理できます。

この論点での、過去の判例(可能であれば、確定した判例)があれば教えて下さい。

あります。
しかも、この論点にかなり「ドンピシャ」で対応する確定判例が存在します。


■ 代表的確定判例

「参議院議員 経歴詐称事件」

👉 最高裁 第二小法廷 判決(平成6年7月18日) (senkyo.win)

これは、公職選挙法235条(虚偽事項公表罪)に基づき、

経歴詐称(=学歴等)による有罪が確定した事件

です。


■ 事案の概要

参議院議員選挙において、

候補者が自身の経歴(学歴を含む)について
虚偽の内容を公表して選挙活動を行った。

その結果、

  • 地裁:有罪(禁錮6月・執行猶予4年) (senkyo.win)
  • 高裁:控訴審でも有罪 (senkyo.win)
  • 最高裁:上告棄却 → 有罪確定 (senkyo.win)

■ 法的争点(まさに今回の論点)

この事件では、

👉 経歴の虚偽表示が
👉 選挙における「虚偽事項公表」に当たるか

が争われました。


■ 最高裁の立場(重要)

裁判所は、

経歴(学歴を含む)は

👉 有権者の判断材料となる重要事項

であり、

虚偽の公表は

👉 選挙の公正を害する

と認定。

つまり、

学歴は

単なる社会的シグナルではなく

法的に保護される情報

と位置付けられました。


■ 法的評価の核心

この判例の意義は明確です。

裁判所は

「学歴が重要かどうか」

という社会的価値論ではなく、

👉 有権者の選択過程に影響しうる情報

という制度的観点から評価しています。


■ その帰結

結果として、

学歴は

  • 能力シグナルかどうかではなく
  • 選択判断に関係する情報

である以上、

虚偽表示は違法となり得る

という整理が確立。


■ 他の実例(確定有罪)

例えば、

1992年参院選では、

学歴詐称により在宅起訴され、

有罪判決が確定し、

👉 当選無効

となったケースも存在します。 (選挙ドットコム)


■ 判例から導かれる法的フレーム

判例の立場は、極めて明確に


社会:

学歴はシグナル

法:

学歴は選択情報


であり、

虚偽表示は

👉 選挙過程への不正入力

として処理される。


■ 本件への示唆

したがって、

今回のような問題も

「学歴の価値論争」ではなく、

判例的には

👉 選択過程の情報の真実性

という軸で判断されることになります。


必要であれば、

この判例のロジックを

今回のケースに当てはめた

勝敗分岐点

も整理できます。

あれ? このような確定判決があるのであれば、今回の伊東市の田久保眞紀 前市長の裁判(が実施されれば)の帰趨は決まったようなものではないのですか。

良いところに気づかれています。
結論から言えば――

「似た判例がある」ことと「結論が決まる」ことは全く別です。

今回のような学歴詐称問題では、確定判例があっても帰趨は自動的には決まりません。理由はシンプルで、

👉 公職選挙法235条は“形式犯”ではなく、“機能犯”に近いからです。

つまり、「虚偽があった」だけでは足りず、選挙との関係性が個別具体的に問われます。


なぜ結論は決まらないのか

平成6年の参院選経歴詐称事件が示したのは、

経歴(学歴を含む)は有権者の判断材料になり得る

という“法的枠組み”です。

しかし、その判例も

すべての学歴詐称が違法

とは言っていません。

違法になるかは、以下の事実認定に依存します。


裁判所が見る「三つの関門」

公職選挙法235条の成立には、実務上、次の三点が核心になります。

① 虚偽性の明確さ

  • 単なる誤記か
  • 表現の解釈余地があるか
  • 経歴の範囲(在籍/修了等)の混同はないか

ここが曖昧だと成立は難しくなります。


② 公表行為の主体性

  • 本人が積極的に表示したか
  • 陣営の作成物か
  • 訂正機会があったか

“誰がどう公表したか”は極めて重要。


③ 当選目的との結び付き

ここが最大の勝負所。

  • 選挙公報に載せたか
  • 演説で強調したか
  • 支持獲得の文脈で使われたか

単なる経歴掲載では足りず、

👉 当選のための手段として機能したか

が問われます。


判例がある場合の実務的意味

判例があると、

  • 法的評価の方向性は示される
  • 「経歴は重要情報になり得る」という前提は共有される

しかし、

👉 違法性は個別事実に依存

します。

これは医療過誤判例と同じ構造で、

「理論」は共通でも
「結果」は症例ごとに異なる。


本件で決定的になる可能性のある点

仮に裁判になれば、帰趨を分けるのは、

  • 学歴の表示の具体的態様
  • 選挙での使用頻度
  • 支持形成との関係

です。

つまり、

学歴を“持っていた”ことではなく
学歴を“使った”こと

が焦点になります。


シンプルにまとめると

判例は

「道筋」を示すが、

「ゴール」は決めない。


法的にはこうなる

判例:

学歴は選択情報になり得る

今回の裁判(仮):

実際に選択情報として使われたか?


この「使われ方」の事実認定こそが、最終的な帰趨を決めます。

必要であれば、
起訴される場合/されない場合の現実的分岐ラインも整理できます。

ありがとうございました。かなり整理できました。また同じようなことを質問するかもしれませんが、よろしくお願いします。

どういたしまして。整理のお役に立てたなら何よりです。

この種のテーマは、事実関係の積み上げと法的評価の切り分けで見え方が大きく変わるので、また新しい情報や疑問が出てきた際に一緒に整理していければと思います。

いつでもどうぞ。

2026,江端さんの忘備録

現在、私は、大学の先生にお願いされて、イベント出席用の管理システムを設計しています。
Currently, I am designing a management system for event attendance at the request of a university professor.

AWSは高価で設定が面倒くさいので、サーバ実装にはAmazon Lightsail(以下、Lightsail)を使っています。
Well, since AWS is expensive and troublesome to configure, I am using Amazon Lightsail (hereafter, Lightsail) for the server implementation.

先日、4月から高校生になる若者が「サーバを作りたい」と言っていたので、Lightsailを勧めたのですが、このLightsailの説明に、ちょっと悩みました。
The other day, a young person who will become a high school student next year said he wanted to build a server, so I recommended Lightsail, but I struggled a bit with how to explain it.

---

以前、今は亡き父と母に、メールシステムを教えることを試みて、それに失敗したのを覚えています。
I remember trying to teach my now-deceased father and mother about email systems and failing.

「メールシステムというのは、世界中のメールサーバを介してメールを配送するシステムで、メールが到着したサーバにアクセスすることで、メールを自分のパソコンに取り込むシステム」と言いながら ――
While saying, "An email system delivers messages via mail servers around the world, and by accessing the server where the mail arrives, you retrieve it to your own computer." 

父と母が『全然分かりません』という表情をしていて、
My parents had expressions that said, "We don't understand at all,"

そして、私も『この説明では全然分からんだろう』と思いながら説明していました。
And I, too, was explaining while thinking, "This explanation won't make sense at all."

で、まあ、母に至っては、「やっぱり、智一(私)がアメリカから送るメールは時間がかかるのかねえ」といっていて ――
And my mother even said, "So emails you send from America take time to arrive, don't they?" 

うん、たしかに、サーバをホップしていくので、それは時間がかかるとは思うけど、それは、「ミリ秒か遅くても秒の単位」だからと、母の認識は"正しく"、そして"間違っていた"のです。
Yes, it does hop across servers so that it might take time, but that time is in milliseconds or at most seconds, so her understanding was both "correct" and "incorrect."

あの時、私は、どのように、メールシステムについて、父と母に説明すべきだったのか、ChatGPTに聞いてみました。
At that time, I asked ChatGPT how I should have explained email systems to my parents.

ChatGPT:『メールは、「インターネットという道路網を通って、相手の近くにある受け取り用の箱まで運ばれる手紙」です。送るとネットワークの中を移動して相手専用の箱に入り、相手は自分のパソコンからその箱を見に行って取り出して読みます。』
ChatGPT: "Email is like a letter that travels through a road network called the Internet to a mailbox near the recipient. When sent, it moves through the network into the recipient's personal box, and they access it from their own computer to retrieve and read it."

「うーむ、ChatGPTでも"ダメか"」と感じました。
I felt, "Hmm, even ChatGPT can't solve this."

問題は、インターネットという概念を知らない人に、それを抜きに説明するのが、恐しく難しい、ということです。
The problem is that it is extremely difficult to explain something to someone who doesn't know what the Internet is.

---

しかし、こちらは、インターネットがもはや、生活インフラとなっている中学生です。この辺は困りませんでした。
However, in this case, we are dealing with a junior high school student for whom the Internet is already a daily part of their infrastructure, so this was not a problem.

私の原案は以下の通りです。
My original proposal was as follows.

■江端案(原案)
■Ebata Proposal (Original)

Amazon Lightsailとは、世界中に配置されているデータセンタの中の一つ(今回は東京リージョン)の中で、24時間ずっと動き続ける自分用のPCのことです(シャットダウンしない)。
Amazon Lightsail is your personal PC that runs continuously, 24 hours a day, in one of the data centers located around the world (this time in the Tokyo region) (it does not shut down).

このPCは、インターネットに直接つながっているので、そこで作ったプログラムは世界中の人から使用されます。
This PC is directly connected to the Internet, so programs created there can be used by people all over the world.

このように24時間いつでも誰でも使えるプログラムのことを、「サーバー」と言います。
Such programs that are available to anyone, anytime are called "servers."

つまりAmazon Lightsailとは、自分で作ったプログラムをサーバーとして世界中の人に使ってもらえる、Amazon社が提供するサーバー基盤のことです。
In other words, Amazon Lightsail is a server platform provided by Amazon that lets programs you create run as servers for people around the world.

これ(原案)に対するChatGPT案(校正案)は以下の通りです。
In response to this (original proposal), ChatGPT's revised proposals were as follows.

■ChatGPT案(その1)
■ChatGPT Proposal (1)

Amazon Lightsailとは、世界中にあるAmazonのデータセンターの中で、24時間ずっと動き続ける「自分専用のコンピューター」を借りることができるサービスです(今回は東京にあるものを使います)。
Amazon Lightsail is a service that lets you rent your own dedicated computer that runs continuously, 24 hours a day, in Amazon's data centers around the world (this time we use one in Tokyo).

このコンピューターはインターネットに直接つながっているため、そこで作ったプログラムを、世界中の人からアクセスできるようにすることができます。
Because this computer is directly connected to the Internet, programs created there can be accessed by people around the world.

このように、インターネットを通して他の人が使えるように動いているコンピューターやプログラムのことを「サーバー」と呼びます。
Such computers or programs that operate so others can use them via the Internet are called "servers."

つまりAmazon Lightsailとは、自分で作ったプログラムをインターネット上で公開し、他の人が使えるようにするためのコンピューターを提供してくれる、Amazonのサービスです。
In other words, Amazon Lightsail is an Amazon service that provides a computer for publishing your programs on the Internet so others can use them.

■ChatGPT案(その2)
■ChatGPT Proposal (2)

Amazon Lightsailとは、インターネットの中に置ける「自分専用のゲームサーバー用マシン」のようなものです。
Amazon Lightsail is like your own dedicated game server machine that you can place on the Internet.

たとえば、マインクラフトのマルチプレイサーバーを動かすには、ずっと電源が入ったままのパソコンが必要になります。しかも、そのパソコンはインターネットにつながっていて、友達がいつでもアクセスできる状態でなければなりません。
For example, running a Minecraft multiplayer server requires a computer that is always powered on and connected to the Internet so friends can access it at any time.

でも、自分の家のパソコンを24時間つけっぱなしにするのは大変です。
But keeping your home computer on 24 hours a day is difficult.

そこで使うのがAmazon Lightsailです。
That's where Amazon Lightsail comes in.

これは、Amazonのデータセンターの中にある「ずっと動き続けているパソコン」を、自分専用に1台借りられるサービスです。
It is a service that lets you rent your own always-on computer in Amazon's data center.

そのパソコンは最初からインターネットにつながっているので、そこにプログラムを置けば、世界中の人がアクセスできます。
Since that computer is already connected to the Internet, installing a program on it allows access from anywhere in the world.

このように、みんながネット経由で使えるように動いている仕組みのことを「サーバー」と呼びます。
Such a mechanism that runs so everyone can use it over the network is called a "server."

つまりAmazon Lightsailとは、
In other words, Amazon Lightsail is

自分だけの「ネット上のゲームサーバー用PC」を借りて、そこに自分の作ったプログラム(たとえばAPI)を置き、スマホアプリやWebサイトから使えるようにできるサービスです。
a service that allows you to rent your own "game server PC on the Internet," place your own programs (such as APIs) on it, and make them usable from smartphone apps or websites.

---

うん、いずれも悪くはない、と思いました。
I thought that none of these were bad.

インターネットを理解していない世代と、理解している世代 ―― 説明の仕方も随分変わるものだなぁ、と実感しています。
I realized that the way explanations change greatly between generations who understand the Internet and those who do not.

もっとも「私の説明能力が、世代に関係なく常に不足している」という可能性も否定できませんが。
However, I cannot deny the possibility that my explanatory ability is always insufficient regardless of generation.

Amazon Web Service(AWS)の無料期間が経過していて、3~4回くらい、5000円/月の課金を喰らっていたことを知って、先日あわててアカウントを抹消しました。

2020/10,江端さんの技術メモ

Amazon Web Service(AWS)の無料期間が経過していて、3~4回くらい、5000円/月の課金を喰らっていたことを知って、先日あわててアカウントを抹消しました。

# 練習用に使っていただけなに、バカみたいです。

ところで、私が今、個人のWebサイトで利用させて頂いているのは、さくらインターネットの月額500円の仮想サーバです。WordPressと併用できて、とても便利です。

ただ、以前、このサーバを使って実家監視システムを作ろうと思ったのですが、色々問題があって使えませんでした(ホームページ用の仮想サーバなんだから当り前)。

といって、AWSは、高額の上に設定が面倒。このクラウドに、どれだけの時間を持っていかれたことか

という訳で調べたら、Amazonにも3.5ドル/月のサーバがあることが分かりました。 Amazon Lightsail です。

うん、これ、いい。私は、サーバを攻撃から守る気もないし、システム破られて持ち出された情報なんぞ入れる気もない。EC2なんぞ1つあれば十分だし、DBはUbuntuの中に作りこんでしまえばいい。

そもそも、ファイアウォールとして使っているロードバランサの設定も、Route53の設定もやりたくありません。

という訳で忘れない内にAmazon Lightsail の作成手順をメモしておきます。

(1)AWSのアカウントを作成(省略)

(2)Amazon Lightsail を選んで、「インスタンスの作成」ボタンを押下
https://lightsail.aws.amazon.com/ls/webapp/home/instances

私は、Ubuntu20.04を選択

一番安い$3.5を選択

あとは、ほったらかしにして(入力値はデフォルト、タグは無視)「インスタンスの作成」ボタンを押下

で、「データベース」タブは無視して、「ネットワーキングタブ」を選択

「静的IPの作成」ボタンを押して、そのままIPアドレスを作る。ちなみに、pingでは応答しないようです。

次に「DNSゾーン」の作成ですが、私は、お名前.comに1つドメイン名を確保しているので、それを使うことにしました。

そのやり方については、これを参考にして下さい。

基本的には、この情報を、お名前.comの方に書き込むだけです。

DNSレコードは、Aレコードだけを設定すれば良いです(サブドメインを作りたい場合は別)。

結果として、こんな表示が出せれば正解

DNSの反映されるに、10分~20分ってところだったと思います。

それと、どこでやったか忘れてしまったけど、SSHアクセス用の秘密鍵をダウンロードするプロセスがでてくるので、それをダウンロードしておく。LightsailDefaultKey-ap-northeast-1.pem てな名前でした(ダウンロード後、リネームしておくと良いでしょう)。

それを使ってTeraTermで、カーネルにアクセスできるようになります。FTPもteratermから "ファイル" → "SSH SCP"を選ぶと、ファイル転送できます(FFFTPは使えません)

あとは、TeraTermでSSHログインして、こんなコマンド投入しておきました。

>sudo apt-get update
>sudo apt install net-tools
>sudo apt install emacs
>sudo apt install golang-go

ロードバランサを使うと$18/月も取られるようなので、OS剥き出しで運用することにしました。

ポートのオープンはこちらからできます。ローカルのテストで http://localhost:8080 とかを使っていましたので、以下のように8080をオープンしておきます。これで、http://sea-anemone:8080 とかが使えるようになります。

(因みに、IPアドレスはデタラメな値に差し替えています)

なお、go言語で作っているサーバの方ですが、以下のような書き換えだけで対応できるようです。

//var addr = flag.String("addr", "localhost:8080", "http service address") // これは動かない
//var addr = flag.String("addr", "0.0.0.0:8080", "http service address") // ローカルでの環境
var addr = flag.String("addr", ":8080", "http service address") // AWS lightsailでの環境

お金がかかるので不満なのですが、システムが丸ごと壊れるのが怖いので、やっぱりスナップショットは1つ作っておくことにしました。

 

未分類

このコンテンツはパスワードで保護されています。閲覧するには以下にパスワードを入力してください。

2026,江端さんの忘備録

新しく上司になった人間は、新しいことを始めたがります。
People who have just become supervisors tend to start new initiatives.

それは、組織を良くしようという善意からであることが多いのですが、現場にとっては必ずしも福音ではありません。
This is often driven by good intentions to improve the organization, but it is not necessarily a blessing for those on the front lines.

なぜなら、新しく導入される「やり方」は、多くの場合、上司にとって良いやりかたではあっても、部下にとっては面倒なことが多いからです。
Because newly introduced "ways of doing things" may be beneficial for the supervisor, but they are often troublesome for subordinates.

「新しく導入される"やり方"」は、良いこともあります。しかし、これは善し悪しの問題ではありません。
These "newly introduced methods" may sometimes be good. However, this is not a matter of good or bad.

しかし、その変更に付き合わされる現場は、手順の再学習、運用の調整、想定外のトラブル対応など、多大な負荷を背負うことになります。
However, the people on the ground who must adapt to these changes end up bearing a heavy burden relearning procedures, adjusting operations, and handling unexpected troubles.

要するに、
In short,

―― 面倒くせい
"it's a hassle."

です。
That's what it comes down to.

---

「改革」というのが、多くの場合上手くいかないのは、大きく2つ理由があります
There are broadly two reasons why "reforms" often fail.

(1)「改革」は、成功するか失敗するか分からない。先例がないから。
(1) A reform may succeed or fail; there is no precedent.

(2)成功するか失敗するか、どちらにせよ、新しいことを強いられるのは面倒で不愉快だから
(2) Whether it succeeds or fails, being forced into something new is troublesome and unpleasant.

既存の方式が、それなりに機能しているのであれば、わざわざ「改革」なんぞ望みません。
If the existing system is functioning reasonably well, no one goes out of their way to seek reform.

「改革」は誰のためにあるのか ーー 畢竟(ひっきょう)、「改革を試してみたい人のため」"だけ"にあります。
For whom does reform exist? Ultimately, it exists only for those who want to try reform.

加えて、もう一つ理由を加えるのであれば、
If one more reason were to be added,

(3)「改革」が成功したとしても、それが大きく評価されることは極めてまれである。その「改革」は、後発的に『誰がやっても同じだった』として、歴史の中に埋没されていくから
(3) Even if a reform succeeds, it is rarely highly valued; it later gets buried in history as something "anyone could have done."

もあるでしょう。
That too may be the case.

いわゆる、「コロンブスの卵」というやつです。これは、改革を目指す人にとっても、負の動機(改革を実施しない言い訳)になると思います。
This is what is called the "Columbus's egg." It can even become a negative motivator, an excuse not to implement reform.

---

冒頭に上げた事例も含めて、こうした営みは「政治改革」です。
Including the example mentioned at the beginning, such efforts are "political reform."

政治とは、別に国会や行政で行われているものだけでなく、会社の部や課のレベルは勿論、家族や、友人間でも行われるものであり、そして、多くの場合「政治改革」は、
Politics is not only what happens in parliament or government; it takes place within company departments, families, and among friends, and in many cases, "political reform":

(1)実施されない
(1) Is never implemented,

(2)実施されて、失敗する
(2) Is implemented and fails,

(3)実施されて、成功して、すぐに忘れ去られる
(3) Is implemented, succeeds, and is quickly forgotten.

の3つの運命しかありません。
These are the only three possible outcomes.

以上、「政治改革の被害者」と「政治改革の実施者」の両方の視点からの分析をしてきました。
Thus far, this has been an analysis from both the victim's and the implementer's perspectives of political reform.

---

私も若いころは、自分の能力の範囲での「政治改革の実施者」をやってきたつもりです。それが、家庭内であったり、テニスサークルであったり、自分の課の中であったりと ーー 随分とチンケなレベルではありますが。
When I was younger, I too considered myself an implementer of "political reform" within my limited capacity, whether at home, in a tennis club, or within my department, though admittedly at a rather trivial level.

ただ、歳を経ることによって、上記の「政治改革」の3つの背景と3つのアウトプットを感じるようになってきて、いつしか『改革者側に立つのは、面倒くさい』と思うようになってきました。
However, as I grew older, I began to sense the three backgrounds and three outcomes of such "political reform," and gradually came to feel that standing on the reformer's side was simply a hassle.

この『面倒くさい』が最高レベルに達した時に、私が至った一のパラダイムが、
When this sense of hassle reached its peak, I arrived at a paradigm:

―― たった一人の政治改革
"political reform by just one person."

です。
That was it.

---

たとえば、私が課内の「政治改革の実施者」であった頃の話です。
For example, when I was acting as a reformer within my department:

ある時、私は、「会議が多すぎる」という、誰もが一度は思う問題に直面しました。
At one point, I faced the common complaint: "There are too many meetings."

週次会議、進捗会議、レビュー会議、報告会議 ーー 名称は違いますが、要するに全部同じです。
Weekly meetings, progress meetings, review meetings, reporting meetings, different names, but essentially the same.

私は、これを改革しようとしました。
I tried to reform this.

まず、(1)会議時間の半減、(2)事前資料の提出義務化、(3)発言者の事前登録、(4)決定事項の即時ログ化、という、『効率化された新会議制度』を設計しました。
First, I designed an "efficient new meeting system": (1) halving meeting time, (2) mandatory pre-submission of materials, (3) pre-registration of speakers, and (4) immediate logging of decisions.

資料を作り、関係者に説明し、導入しました ―― 結果、『会議は半減しませんでした』。
I prepared materials, explained them to stakeholders, and implemented the results: meetings were not halved.

事前資料は提出されず、発言者登録は形骸化し、ログは誰も見ませんでした。
Materials were not submitted, speaker registration became a formality, and no one looked at the logs.

そして何より、『新しい運用ルールの説明のための会議』が追加されました。
Above all, a "meeting to explain the new rules" was added.

つまり、私は「会議を減らす改革」によって、『会議を増やした』のです。
In short, through a reform meant to reduce meetings, I increased them.

―― うん、バカとしか言いようがない
"Yes, there's no way to describe it except as foolish."

"反省"や"自己批判"という言葉を越えて「呆れて笑うしかない」 ―― "自嘲自棄(じちょうじき)"です。
Beyond "reflection" or "self-criticism," it left me only able to laugh in exasperated self-mocking despair.

---

その後の私は、一切の会議改革を提案することをやめました。
After that, I stopped proposing any reforms to meetings.

その代わり、自分の出席する会議だけ、「本当に必要なこと以外は発言しない」という運用を開始しました。
Instead, I began operating under a rule in meetings I attended: not to speak unless necessary.

(正確にいうと『議論の発散を防ぎ、収束する方向に姑息に誘導する』運用ですが)
(More precisely, I subtly steered discussions to prevent divergence and move toward closure.)

驚くべきことに、私が発言しなくても、会議は進行し、決定もなされ、議事録も問題なく作られました。
Remarkably, even without my speaking, meetings proceeded, decisions were made, and minutes were created without issue.

何一つ問題は起きませんでした。それどころか、会議時間は短縮されました。
No problems arose; in fact, meeting times were shortened.

私が、説明や補足は新規な事項を言い出さないように努めた結果、議論が拡散しなかったのです(議論を「発展させなかった」とも言えますが)。
By refraining from introducing new points in explanations or clarifications, discussions did not spread outward (or one could say they did not "develop").

私は、会議制度を変えなかったにも関わらず、会議の運用を変えてしまったのです ―― 私の主催する会議についてだけ、ですが。
Without changing the meeting system, I had changed its operation at least for the meetings I chaired.

---

その後、「江端さんは、会議を時間以内にきっきりまとめる」という評価を受けるようになったという話も聞きましたが、私は何もしていません。
Later, I heard that I was evaluated as someone who neatly wraps up meetings within the allotted time, but I did nothing.

本当に「何もしなかった」。
Truly, I did nothing.

制度を変えるのではなく、改革者を一人減らす ―― その改革者が、たまたま私だった、というだけの話です。
Rather than changing the system, I reduced the number of reformers by one; that reformer just happened to be me.

---

改革を否定する改革 ―― これが、私の至った「たった一人の政治改革」です。
A reform that denies reform, that is, the "political reform by just one person," I arrived at.

『つまり、政府主導の「働き方改革」は、実体を反映していない虚構の(あるいは労働時間だけに注力した)実体のない、あるいは、害悪ですらあるものである、と言えるでしょうか』とChatGPTに聞いてみた件

未分類

https://wp.kobore.net/2025/12/10/post-23101/

HLS映像検索・サムネイル・再生システム構築手順書 - リバースプロキシ対応による公開境界分離構成

HLS映像検索・サムネイル・再生システム構築手順書 - リバースプロキシ対応による公開境界分離構成.md

以下は、貼付「HLS映像検索・サムネイル・再生システム構築手順書.pdf」を前提に、**“今回の修正(= reverse-proxy 化)で追加・変更した箇所だけ”**を、**手順書内にそのまま貼り付けられる形(ファイル全文+コマンド全文)**でまとめたものです。
(方針は、貼付「映像検索・配信システム構築手順書.pdf」の reverse-proxy(Nginx)で API を隠蔽する方式を踏襲しています 。)


0. 背景と目的(今回の差分の意義)

背景

従来手順では、FastAPI が HLS(/hls)やサムネイル(/thumbs)やUI(/ui)を直接配信する構成になっており 、ブラウザからのアクセス経路が「API 直アクセス」になりがちです。また DB もホストに port 公開して運用しがちです(例:15432:5432) 。

目的(今回)

  • ブラウザから見える公開点を **http://localhost/(reverse-proxy)に一本化**する
  • API/DB/HLS/サムネイルを“フロントから直接見えない”(= 直接ポート露出や直配信を避ける)構造へ寄せる
  • 方式は「映像検索・配信システム構築手順書.pdf」の reverse-proxy 構成(location /api/ { proxy_pass ... } 等)を踏襲

1. 今回の修正で追加するディレクトリ

既存 ~/video_hls_project の直下に、以下を 新規作成します。

cd ~/video_hls_project

mkdir -p api
mkdir -p reverse-proxy/frontend

2. 今回の修正で「新規に作る(または置き換える)」ファイル一覧

  • api/Dockerfile(新規)
  • api/app.py(新規:※従来の backend_api/app.py を “コンテナ運用前提” にした版)
  • reverse-proxy/Dockerfile(新規)
  • reverse-proxy/nginx.conf(新規)
  • reverse-proxy/frontend/index.html(新規:UIをreverse-proxy側に同梱)
  • docker-compose.yml(置き換え)

3. ファイル全文:api/Dockerfile(新規)

作成先: ~/video_hls_project/api/Dockerfile

cat << 'EOF' > ~/video_hls_project/api/Dockerfile
FROM python:3.11-slim

WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential libpq-dev \
 && rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir \
    fastapi uvicorn psycopg2-binary python-multipart

COPY app.py /app/app.py

EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
EOF

4. ファイル全文:api/app.py(新規)

作成先: ~/video_hls_project/api/app.py

ポイント:

  • DB内 m3u8_path(例:videos_hls/clip01/index.m3u8)を、返却URL /hls/clip01/index.m3u8 に変換するロジックは従来PDFの考え方を踏襲しています
  • /hls /thumbs を FastAPI で静的マウントする方式自体は従来PDF同様
  • ただし公開は reverse-proxy 経由に統一します(後述 nginx.conf)
cat << 'EOF' > ~/video_hls_project/api/app.py
from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
from typing import Optional
import psycopg2
import psycopg2.extras
import os
from pathlib import Path

# ========= パス設定(コンテナ内) =========
# docker-compose で /data にホストの video_hls_project をマウントする前提
BASE_DIR = Path("/data")

HLS_DIR   = BASE_DIR / "videos_hls"
THUMB_DIR = BASE_DIR / "thumbnails"

# ========= DB 接続設定(コンテナ内から db サービスへ) =========
DB_HOST = os.getenv("DB_HOST", "db")
DB_PORT = int(os.getenv("DB_PORT", "5432"))
DB_NAME = os.getenv("DB_NAME", "video_db")
DB_USER = os.getenv("DB_USER", "video_user")
DB_PASS = os.getenv("DB_PASS", "password")

def get_conn():
    return psycopg2.connect(
        host=DB_HOST,
        port=DB_PORT,
        dbname=DB_NAME,
        user=DB_USER,
        password=DB_PASS,
    )

app = FastAPI()

@app.get("/health")
def health():
    return {"status": "ok"}

# ========= 静的ファイル =========
# (従来手順では FastAPI が直配信していたが、今回は reverse-proxy 経由でのみ公開する)
app.mount("/hls", StaticFiles(directory=HLS_DIR), name="hls")
app.mount("/thumbs", StaticFiles(directory=THUMB_DIR), name="thumbs")

def to_hls_url(m3u8_path: str) -> str:
    """
    DBの m3u8_path (例: videos_hls/clip01/index.m3u8)
    を、ブラウザ向け URL (/hls/clip01/index.m3u8) に変換する。
    (従来手順書の変換仕様を踏襲)
    """
    prefix = "videos_hls/"
    if m3u8_path.startswith(prefix):
        rel = m3u8_path[len(prefix):]   # "clip01/index.m3u8"
    else:
        rel = m3u8_path
    return f"/hls/{rel}"

def to_thumb_url(chunk_id: str) -> str:
    """
    チャンク代表サムネイルは 00000.jpg を返す(従来通り)。
    """
    return f"/thumbs/{chunk_id}/00000.jpg"

@app.get("/api/chunks")
def list_chunks(tag: Optional[str] = None):
    """
    映像一覧API
    - /api/chunks
    - /api/chunks?tag=test
    """
    conn = get_conn()
    try:
        cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
        if tag:
            sql = """
            SELECT camera_id, chunk_id, title, m3u8_path, duration_sec, tags, created_at
            FROM train_camera_chunk
            WHERE tags @> ARRAY[%s]::text[]
            ORDER BY id;
            """
            cur.execute(sql, (tag,))
        else:
            sql = """
            SELECT camera_id, chunk_id, title, m3u8_path, duration_sec, tags, created_at
            FROM train_camera_chunk
            ORDER BY id;
            """
            cur.execute(sql)

        rows = cur.fetchall()
        result = []
        for row in rows:
            d = dict(row)
            d["hls_url"] = to_hls_url(d["m3u8_path"])
            d["thumb_url"] = to_thumb_url(d["chunk_id"])
            result.append(d)
        return result
    finally:
        conn.close()

@app.get("/api/chunks/{chunk_id}")
def get_chunk(chunk_id: str):
    """
    chunk_id(clip01 等)で1件取得
    """
    conn = get_conn()
    try:
        cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
        sql = """
        SELECT camera_id, chunk_id, title, m3u8_path, duration_sec, tags, created_at
        FROM train_camera_chunk
        WHERE chunk_id = %s;
        """
        cur.execute(sql, (chunk_id,))
        row = cur.fetchone()
        if not row:
            raise HTTPException(status_code=404, detail="chunk not found")

        d = dict(row)
        d["hls_url"] = to_hls_url(d["m3u8_path"])
        d["thumb_url"] = to_thumb_url(d["chunk_id"])
        return d
    finally:
        conn.close()

@app.get("/api/chunks/{chunk_id}/segments")
def get_segments(chunk_id: str):
    """
    指定 chunk の index.m3u8 を読み、TSセグメント情報を返す。
    (従来手順書にある /api/chunks/{chunk_id}/segments の考え方を踏襲)
    """
    m3u8_path = HLS_DIR / chunk_id / "index.m3u8"
    if not m3u8_path.exists():
        raise HTTPException(status_code=404, detail="m3u8 not found")

    segments = []
    current_start = 0.0

    with m3u8_path.open("r", encoding="utf-8") as f:
        lines = [line.strip() for line in f if line.strip()]

    i = 0
    idx = 0
    while i < len(lines):
        line = lines[i]
        if line.startswith("#EXTINF:"):
            dur_part = line.split(":", 1)[1]
            dur_str = dur_part.split(",", 1)[0]
            try:
                dur = float(dur_str)
            except ValueError:
                dur = 0.0

            if i + 1 >= len(lines):
                break

            ts_name = lines[i + 1]  # "00000.ts" 等

            segments.append({
                "index": idx,
                "ts": ts_name,
                "start_sec": current_start,
                "duration_sec": dur,
                "thumb_url": f"/thumbs/{chunk_id}/{ts_name.replace('.ts', '.jpg')}",
            })

            current_start += dur
            idx += 1
            i += 2
        else:
            i += 1

    return segments
EOF

5. ファイル全文:reverse-proxy/Dockerfile(新規)

作成先: ~/video_hls_project/reverse-proxy/Dockerfile

(「映像検索・配信システム構築手順書.pdf」の Nginx コンテナ作り方を踏襲 )

cat << 'EOF' > ~/video_hls_project/reverse-proxy/Dockerfile
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY frontend /usr/share/nginx/html
EOF

6. ファイル全文:reverse-proxy/nginx.conf(新規)

作成先: ~/video_hls_project/reverse-proxy/nginx.conf

ポイント:

  • /api/api:8000 に reverse-proxy(方式は踏襲)
  • /hls//thumbs/ も api 側へ proxy(= ブラウザは reverse-proxy 以外に触れない)
cat << 'EOF' > ~/video_hls_project/reverse-proxy/nginx.conf
events {}

http {
  server {
    listen 80;

    # ---- UI(静的)----
    location / {
      root /usr/share/nginx/html;
      index index.html;
      try_files $uri $uri/ /index.html;
    }

    # ---- API ----
    location /api/ {
      proxy_pass http://api:8000/;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # ---- HLS(m3u8/ts)----
    location /hls/ {
      proxy_pass http://api:8000/hls/;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # ---- Thumbnails(jpg)----
    location /thumbs/ {
      proxy_pass http://api:8000/thumbs/;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
  }
}
EOF

7. ファイル全文:reverse-proxy/frontend/index.html(新規)

作成先: ~/video_hls_project/reverse-proxy/frontend/index.html

※UIは reverse-proxy 配下で配信し、API は /api/... を叩きます(踏襲の方向性:reverse-proxyで公開点を分離 )。

cat << 'EOF' > ~/video_hls_project/reverse-proxy/frontend/index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>HLS映像検索・サムネイル・再生</title>
  <style>
    body { font-family: sans-serif; margin: 16px; }
    .row { display: flex; gap: 16px; }
    .col { flex: 1; min-width: 320px; }
    .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
    .card { border: 1px solid #ddd; padding: 8px; cursor: pointer; }
    .card:hover { background: #fafafa; }
    img { max-width: 100%; height: auto; display: block; }
    video { width: 100%; background: #000; }
    .muted { color: #666; font-size: 12px; }
  </style>
</head>
<body>
  <h1>HLS映像検索・サムネイル・再生</h1>

  <div style="margin: 8px 0;">
    <label>タグ: <input id="tagInput" placeholder="例: test" /></label>
    <button id="searchBtn">検索</button>
    <span id="status" class="muted"></span>
  </div>

  <div class="row">
    <div class="col">
      <h2>■ サムネイル表示部(映像単位)</h2>
      <div id="chunkGrid" class="grid"></div>
    </div>

    <div class="col">
      <h2>■ セグメントサムネイル表示部(選択中の映像)</h2>
      <div id="segmentGrid" class="grid"></div>

      <h2 style="margin-top:16px;">■ 再生</h2>
      <video id="video" controls playsinline></video>
      <div id="nowPlaying" class="muted"></div>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
  <script>
    const statusEl = document.getElementById('status');
    const chunkGrid = document.getElementById('chunkGrid');
    const segmentGrid = document.getElementById('segmentGrid');
    const videoEl = document.getElementById('video');
    const nowPlayingEl = document.getElementById('nowPlaying');

    let hls = null;

    function setStatus(msg) { statusEl.textContent = msg || ''; }

    async function fetchJSON(url) {
      const res = await fetch(url);
      if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
      return await res.json();
    }

    function playHLS(url) {
      if (hls) { hls.destroy(); hls = null; }
      if (videoEl.canPlayType('application/vnd.apple.mpegurl')) {
        videoEl.src = url;
      } else if (window.Hls && Hls.isSupported()) {
        hls = new Hls();
        hls.loadSource(url);
        hls.attachMedia(videoEl);
      } else {
        alert('このブラウザはHLS再生に対応していません(hls.jsが使えません)。');
      }
    }

    function clearSegments() {
      segmentGrid.innerHTML = '';
    }

    function renderChunks(chunks) {
      chunkGrid.innerHTML = '';
      chunks.forEach(c => {
        const div = document.createElement('div');
        div.className = 'card';
        div.innerHTML = `
          <img src="${c.thumb_url}" alt="">
          <div><b>${c.title || c.chunk_id}</b></div>
          <div class="muted">${c.chunk_id} / ${(c.tags||[]).join(', ')}</div>
        `;
        div.onclick = async () => {
          setStatus(`segments取得中: ${c.chunk_id}`);
          nowPlayingEl.textContent = `選択中: ${c.chunk_id}`;
          clearSegments();

          // セグメント一覧
          const segs = await fetchJSON(`/api/chunks/${encodeURIComponent(c.chunk_id)}/segments`);
          renderSegments(c.chunk_id, segs);

          // 再生
          playHLS(c.hls_url);
          setStatus('');
        };
        chunkGrid.appendChild(div);
      });
    }

    function renderSegments(chunkId, segs) {
      segmentGrid.innerHTML = '';
      segs.forEach(s => {
        const div = document.createElement('div');
        div.className = 'card';
        const thumb = s.thumb_url;
        div.innerHTML = `
          <img src="${thumb}" alt="">
          <div class="muted">#${s.index} ${s.ts}</div>
          <div class="muted">${s.start_sec.toFixed(2)}s (+${s.duration_sec.toFixed(2)}s)</div>
        `;
        segmentGrid.appendChild(div);
      });
    }

    async function loadChunks() {
      const tag = document.getElementById('tagInput').value.trim();
      const url = tag ? `/api/chunks?tag=${encodeURIComponent(tag)}` : '/api/chunks';
      setStatus('chunks取得中...');
      const chunks = await fetchJSON(url);
      renderChunks(chunks);
      clearSegments();
      nowPlayingEl.textContent = '';
      setStatus(`件数: ${chunks.length}`);
    }

    document.getElementById('searchBtn').onclick = loadChunks;

    // 初期ロード
    loadChunks().catch(e => {
      console.error(e);
      setStatus('初期ロード失敗: ' + e.message);
    });
  </script>
</body>
</html>
EOF

8. ファイル全文:docker-compose.yml(置き換え)

作成先: ~/video_hls_project/docker-compose.yml

ポイント:

  • db / api / reverse-proxy の 3サービス化(reverse-proxy の ports 80:80 のみ公開)
  • db の ports 公開は 原則やめる(必要ならコメント解除で運用)
  • api は ~/video_hls_project/data にマウントして HLS/サムネイルを参照する

(reverse-proxy + api + db の並びは「映像検索・配信システム構築手順書.pdf」の構成を踏襲 )

cat << 'EOF' > ~/video_hls_project/docker-compose.yml
version: "3.9"

services:
  db:
    image: postgis/postgis:16-3.4
    container_name: train-video-db
    environment:
      POSTGRES_DB: video_db
      POSTGRES_USER: video_user
      POSTGRES_PASSWORD: password

    # 重要:原則、ホストへ公開しない(フロントから見えない)
    # 必要ならデバッグ時だけ一時的に開ける:
    # ports:
    #   - "15432:5432"

    volumes:
      - ./db_init:/docker-entrypoint-initdb.d
      - pgdata:/var/lib/postgresql/data

  api:
    build: ./api
    container_name: train-video-api
    environment:
      DB_HOST: db
      DB_PORT: "5432"
      DB_NAME: video_db
      DB_USER: video_user
      DB_PASS: password
    depends_on:
      - db
    volumes:
      # ホストの ~/video_hls_project を /data として参照(HLS/サムネイルを読む)
      - ./:/data:ro

    # 重要:api もホストへ公開しない(reverse-proxy 経由のみ)
    # ports:
    #   - "18000:8000"

  reverse-proxy:
    build: ./reverse-proxy
    container_name: train-video-frontend
    ports:
      - "80:80"
    depends_on:
      - api

volumes:
  pgdata:
EOF

9. 起動方法(今回版)

cd ~/video_hls_project

docker compose down
docker compose up -d --build

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

期待:train-video-frontend だけが 0.0.0.0:80->80/tcp を持ち、db/api は host port を持たない。


10. 確認方法(今回版)

10.1 reverse-proxy 経由で API を確認

curl -i http://localhost/api/health
curl -i http://localhost/api/chunks

10.2 HLS の疎通確認(例:clip03)

curl -I http://localhost/hls/clip03/index.m3u8

10.3 ブラウザ確認

  • Web UI:http://localhost/
  • 映像一覧(JSON):http://localhost/api/chunks

11. 運用上の注意(今回の「フロントから見えない」化の要点)

  • DBはホストへ port 公開しない(原則)
    従来の 15432:5432 公開は、必要時のみ一時的に使う扱いに変更(元手順では公開例あり )。
  • APIもホストへ port 公開しない
    ブラウザは http://localhost/ にしか触れず、/api /hls /thumbs は全て Nginx が中継。

必要なら、次に「HLS映像検索・サムネイル・再生システム構築手順書.pdf」側の **どの章・どの節に、上の差分をどう挿入するか(“差し込み位置メモ”)**も、章番号つきで作れます。