2026年10月21日水曜日

apt の gpg キーを更新しよう

apt の gpg キーを更新しよう

概要

サードパーティのリポジトリを追加している場合は gpg キーが定期的に更新される場合があるので更新してあげましょう

環境

  • Ubuntu 24.04

エラーログ

Get:1 https://cli.github.com/packages stable InRelease [3,917 B]
Hit:2 https://aquasecurity.github.io/trivy-repo/deb generic InRelease
Err:1 https://cli.github.com/packages stable InRelease
  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 5612B36462313325
Hit:3 https://download.docker.com/linux/ubuntu noble InRelease
Get:4 https://apt.releases.hashicorp.com noble InRelease [12.8 kB]
Err:4 https://apt.releases.hashicorp.com noble InRelease
  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY FC9CA96ACA026560
Hit:5 http://repo.mysql.com/apt/ubuntu noble InRelease
Hit:6 http://security.ubuntu.com/ubuntu noble-security InRelease
Hit:7 http://archive.ubuntu.com/ubuntu noble InRelease
Hit:8 http://archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:9 https://ppa.launchpadcontent.net/ansible/ansible/ubuntu noble InRelease
Hit:10 https://ppa.launchpadcontent.net/git-core/ppa/ubuntu noble InRelease
Get:11 https://esm.ubuntu.com/apps/ubuntu noble-apps-security InRelease [8,371 B]
Get:12 https://esm.ubuntu.com/apps/ubuntu noble-apps-updates InRelease [8,220 B]
Get:13 https://esm.ubuntu.com/infra/ubuntu noble-infra-security InRelease [8,235 B]
Get:14 https://esm.ubuntu.com/infra/ubuntu noble-infra-updates InRelease [8,213 B]
Fetched 49.8 kB in 4s (12.9 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
3 packages can be upgraded. Run 'apt list --upgradable' to see them.
W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: https://cli.github.com/packages stable InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 5612B36462313325
W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: https://apt.releases.hashicorp.com noble InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY FC9CA96ACA026560
W: Failed to fetch https://cli.github.com/packages/dists/stable/InRelease  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 5612B36462313325
W: Failed to fetch https://apt.releases.hashicorp.com/dists/noble/InRelease  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY FC9CA96ACA026560
W: Some index files failed to download. They have been ignored, or old ones used instead.

更新コマンド

# GitHub CLIの署名鍵を更新
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
  | sudo install -o root -g root -m 0644 /dev/stdin \
    /etc/apt/keyrings/githubcli-archive-keyring.gpg

# HashiCorpの署名鍵を更新
curl -fsSL https://apt.releases.hashicorp.com/gpg \
  | gpg --dearmor \
  | sudo install -o root -g root -m 0644 /dev/stdin \
    /usr/share/keyrings/hashicorp-archive-keyring.gpg

# 動作確認
sudo apt-get update

最後に

今回は github と hashicorp でしたが他でも同様に更新が必要になるケースがあります

2026年10月20日火曜日

macOS Tahoe クリーンインストールメモ

macOS Tahoe クリーンインストールメモ

概要

前回から4年、今後は Tahoe でやりました

最初に

2026/10/20 時点でクリーンインストールしましたが Tahoe のインストールメディアは不要でした

環境

  • M1 MacBook Air
  • macOS 26.6.2

インストールメディアの準備

一応作っておいてもいいですが基本は不要です
起動オプションを表示し画面のように Tahoe を再インストールできるようになっていれば OK です

  • sudo diskutil eraseDisk FAT32 MYSD MBRFormat /dev/disk4
  • sudo softwareupdate --fetch-full-installer --full-installer-version 26.6
  • sudo /Applications/Install\ macOS\ Tahoe.app/Contents/Resources/createinstallmedia --volume /Volumes/USB

インストールメディア自体を作成するのに1時間ほどかかります (回線やデバイスの性能にもよる

起動オプション付きで起動

  • 一旦シャットダウン
  • 電源ボタンを押し続けて起動

ディスクの消去

  • ディスクユーティリティを起動
  • ディスクユーティリティから Macintosh HD を消去

アクティベーション

  • ディスクユーティリティを閉じるとアクティベーションが開始
  • AppleID などを入力する
  • Wifi などの設定も前のものを引き継いで使用してくれる

Tahoe の再インストール

  • あとは再インストールするだけ
  • 時間は2時間は見たほうがいい
  • 再インストールが完了したらあとは自由に設定すれば OK

最後に

再インストール直後のディスクサイズは40GBほどでした
ディスクが逼迫したら再インストールするのが一番簡単ですが面倒なのも事実です

参考サイト

2026年10月19日月曜日

docker 25 から 29 にアップグレードする方法

docker 25 から 29 にアップグレードする方法

概要

ついに 25 が EOL になったのでアップグレード

環境

  • Ubuntu 22.04

更新先のバージョンの確認

  • apt -y update
  • apt-cache policy docker-ce docker-ce-cli
  • apt list --all-versions docker-ce | head -n 10

更新可能な最新バージョンをメモ

ピン留めの修正

  • cp docker.pref docker.pref.back
  • vim docker.pref
Explanation: Pin added by DevOps with GitLab
Package: docker-ce*
Pin: version 5:29.9.1-1~ubuntu.22.04~jammy
Pin-Priority: 600

更新

  • apt install --dry-run docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  • apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
root@ubuntu:/etc/apt/preferences.d# apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
docker-buildx-plugin is already the newest version (0.38.0-1~ubuntu.22.04~jammy).
docker-buildx-plugin set to manually installed.
docker-compose-plugin is already the newest version (5.6.0-1~ubuntu.22.04~jammy).
docker-compose-plugin set to manually installed.
The following packages were automatically installed and are no longer required:
  libfwupd2 libfwupdplugin5 libgcab-1.0-0 libgusb2 libltdl7 libmbim-glib4 libmbim-proxy libmm-glib0 libqmi-glib5 libqmi-proxy libsmbios-c2 libtcl8.6 modemmanager
  tcl tcl8.6 usb-modeswitch usb-modeswitch-data
Use 'apt autoremove' to remove them.
Suggested packages:
  cgroupfs-mount | cgroup-lite docker-model-plugin
The following packages will be upgraded:
  containerd.io docker-ce docker-ce-cli
3 upgraded, 0 newly installed, 0 to remove and 5 not upgraded.
Need to get 65.4 MB of archives.
After this operation, 22.6 MB of additional disk space will be used.
N: Ignoring file 'docker.pref.back' in directory '/etc/apt/preferences.d/' as it has an invalid filename extension
Do you want to continue? [Y/n] Y
Get:1 https://download.docker.com/linux/ubuntu jammy/stable amd64 docker-ce-cli amd64 5:29.9.1-1~ubuntu.22.04~jammy [17.6 MB]
Get:2 https://download.docker.com/linux/ubuntu jammy/stable amd64 containerd.io amd64 2.4.2-2~ubuntu.22.04~jammy [23.3 MB]
Get:3 https://download.docker.com/linux/ubuntu jammy/stable amd64 docker-ce amd64 5:29.9.1-1~ubuntu.22.04~jammy [24.6 MB]
Fetched 65.4 MB in 1s (48.8 MB/s)
(Reading database ... 109872 files and directories currently installed.)
Preparing to unpack .../docker-ce-cli_5%3a29.9.1-1~ubuntu.22.04~jammy_amd64.deb ...
Unpacking docker-ce-cli (5:29.9.1-1~ubuntu.22.04~jammy) over (5:25.0.5-1~ubuntu.22.04~jammy) ...
Preparing to unpack .../containerd.io_2.4.2-2~ubuntu.22.04~jammy_amd64.deb ...
Unpacking containerd.io (2.4.2-2~ubuntu.22.04~jammy) over (2.4.1-2~ubuntu.22.04~jammy) ...
Preparing to unpack .../docker-ce_5%3a29.9.1-1~ubuntu.22.04~jammy_amd64.deb ...
Unpacking docker-ce (5:29.9.1-1~ubuntu.22.04~jammy) over (5:25.0.5-1~ubuntu.22.04~jammy) ...
Setting up containerd.io (2.4.2-2~ubuntu.22.04~jammy) ...
/usr/sbin/policy-rc.d returned 101, not running 'restart containerd.service'
Setting up docker-ce-cli (5:29.9.1-1~ubuntu.22.04~jammy) ...
Setting up docker-ce (5:29.9.1-1~ubuntu.22.04~jammy) ...
invoke-rc.d: policy-rc.d denied execution of restart.
/usr/sbin/policy-rc.d returned 101, not running 'restart docker.service docker.socket'
Processing triggers for man-db (2.10.2-1) ...
Scanning processes...
Scanning candidates...
Scanning linux images...

Running kernel seems to be up-to-date.

Restarting services...
 systemctl restart fwupd.service irqbalance.service multipathd.service open-vm-tools.service packagekit.service polkit.service rsyslog.service ssh.service systemd-timesyncd.service systemd-udevd.service udisks2.service unbound.service upower.service
Service restarts being deferred:
 systemctl restart ModemManager.service
 systemctl restart containerd.service
 /etc/needrestart/restart.d/dbus.service
 systemctl restart docker.service
 systemctl restart networkd-dispatcher.service
 systemctl restart systemd-logind.service
 systemctl restart unattended-upgrades.service

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries on this host.

docker 再起動

  • systemctl restart containerd.service
  • systemctl restart docker.service

確認

  • docker version
Client: Docker Engine - Community
 Version:           29.9.1
 API version:       1.56
 Go version:        go1.26.9
 Git commit:        dfc520d
 Built:             Wed Oct 14 18:32:26 2026
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          29.9.1
  API version:      1.56 (minimum version 1.40)
  Go version:       go1.26.9
  Git commit:       b26fb77
  Built:            Wed Oct 14 18:32:26 2026
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v2.4.2
  GitCommit:        b7a25261e029d68978bbf290df0a62fc819b3c51
 runc:
  Version:          1.5.2
  GitCommit:        v1.5.2-0-g29dd3dc2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

最後に

今回は Docker CE のバージョンを APT preferences で指定しています。次回以降に更新する場合は、リポジトリの更新候補を確認し、必要に応じてピン留め設定を変更します。

Docker 公式リポジトリは unattended-upgrades の対象にしていないため、自動更新は行われません。

2026年10月6日火曜日

strix 超入門

strix 超入門

概要

https://github.com/usestrix/strix

環境

  • Ubuntu 24.04
  • strix 1.17.0
  • litellm

litellm

インストール

  • curl -sSL https://strix.ai/install | bash

起動

  • export STRIX_LLM="gemini-3.5-flash"
  • export LLM_API_BASE="http://localhost:4000/"
  • export LLM_API_KEY="dummy"

実行

  • strix --target ./api
  • strix --target http://localhost:8080

最後に

  • 普通に攻撃になるのでご注意を
  • トークンはかなり使います
  • コード解析の場合自動で脆弱になりそうな箇所を診断し攻撃してくれます
    • 各種インジェクション
    • 例えば jinja2 テンプレートを使う箇所があれば自動でテンプレートインジェクションできるかチェックしてくれます

最後に

AI x ハックは強すぎる気がします

2026年9月11日金曜日

コミットの author と committer をすべて指定したユーザで書き換える方法

コミットの author と committer をすべて指定したユーザで書き換える方法

概要

.git/config などを設定して無くグローバルの user,email を使ってしまっていた場合に全件 author と committer を書き換えたい場合があるかなと思います
その方法を紹介します

環境

  • macOS 26.6.2
  • git 2.55.0

書き換え

NEW_NAME="your_user_name"
NEW_EMAIL="your_user_email@local"

git stash push -u -m 'temp-before-identity-rewrite' && \
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --force --env-filter "
GIT_AUTHOR_NAME=\"$NEW_NAME\"
GIT_AUTHOR_EMAIL=\"$NEW_EMAIL\"
GIT_COMMITTER_NAME=\"$NEW_NAME\"
GIT_COMMITTER_EMAIL=\"$NEW_EMAIL\"
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
" master && \
git stash pop

確認

0 になれば OK です

git log master --format='%an%x09%ae%x09%cn%x09%ce' |
awk -F '\t' -v new_name="$NEW_NAME" -v new_email="$NEW_EMAIL" \
  '$1 != new_name || $2 != new_email || $3 != new_name || $4 != new_email { count++ }
   END { print count+0 }'

強制上書き

git push -f origin master

後処理

git update-ref -d refs/original/refs/heads/master

最後に

ちゃんと最初に .git/config は書いておきましょう

2026年9月5日土曜日

docker compose で起動したコンテナがどの作業ディレクトリで動いているか確認する方法

docker compose で起動したコンテナがどの作業ディレクトリで動いているか確認する方法

概要

docker compose でいろいろなパスで docker compose up -d していると忘れるので一覧で表示する方法を紹介します

コマンド

docker ps -q | xargs -I {} docker inspect -f '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}' {}

2026年9月4日金曜日

GCP の ArtifactRegistry にある latest タグ以外のイメージを一括で削除する方法

GCP の ArtifactRegistry にある latest タグ以外のイメージを一括で削除する方法

概要

CLI を使いましょう

環境

  • macOS 26.6.2
  • gcloud cli 545.0.0

タグの一覧を表示

gcloud artifacts docker images list \
  asia-northeast1-docker.pkg.dev/product-123456/gae-standard/app/default \
  --include-tags --format='get(version,tags)'

対象のタグに絞り込む

gcloud artifacts docker images list \
  asia-northeast1-docker.pkg.dev/product-123456/gae-standard/app/default \
  --include-tags \
  --format='get(version,tags)' |
while read -r version tags; do
  if [[ "$tags" != *latest* ]]; then
    echo ${tags}
    echo ${version}
  fi
done

絞り込んだイメージを削除

gcloud artifacts docker images list \
  asia-northeast1-docker.pkg.dev/product-123456/gae-standard/app/default \
  --include-tags \
  --format='get(version,tags)' |
while read -r version tags; do
  if [[ "$tags" != *latest* ]]; then
    echo ${tags}
    echo ${version}

    gcloud artifacts docker images delete \
      "asia-northeast1-docker.pkg.dev/product-123456/gae-standard/app/default@$version" \
      --delete-tags \
      --quiet
  fi
done

最後に

プロジェクトのIDやリポジトリ名やパッケージ名は適宜変更してください

2026年9月3日木曜日

Xcode26 で ipa ファイルを作成する方法

Xcode26 で ipa ファイルを作成する方法

概要

なぜか xcode からだとエンロール関係でエラーになるので CLI を使いましょう

環境

  • macOS 26.6.2
  • Xcode 26.6 (17F113)
  • iPhone 15

事前準備

  • Xcode に無料の Developer Account でログイン
  • Xcode で開発用の証明書作成
  • iPhone 実機の接続

ビルド: app フォルダの作成

xcodebuild -workspace ios-moributa.xcworkspace \
           -scheme ios-moributa \
           -sdk iphoneos \
           -configuration Release \
           -archivePath ./build/ios-moributa.xcarchive \
           archive

アーカイブ: ipa ファイルの作成

xcodebuild -workspace ios-moributa.xcworkspace \
           -scheme ios-moributa \
           -sdk iphoneos \
           -configuration Release \
           -archivePath ./build/ios-moributa.xcarchive \
           archive

配布設定ファイルの作成

  • vim ExportOptions.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://apple.com">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>development</string> <!-- 開発用証明書(iOS Development)で書き出す -->
    <key>signingStyle</key>
    <string>automatic</string>
</dict>
</plist>

最後に

ワークスペース名やスキーマ名は適宜変更してください

2026年9月2日水曜日

AltStore で the data couldn’t be read because it isn’t in the correct format

AltStore で the data couldn’t be read because it isn’t in the correct format

概要

対応策を紹介します

環境

  • macOS 26.6.2
  • AltServer 1.7.2
  • AltStore 2.2.1

対応策

  • iPhone の wifi をオフにする
  • Mac と有線接続する
  • AltServer 起動
  • AltStore 起動
  • アプリの更新

最後に

macOS をアップデートすると出る気がしなくもないです

2026年8月30日日曜日

semgrep 超入門

semgrep 超入門

概要

SAST しましょう

環境

  • macOS 26.6.2
  • semgrep 1.175.0

インストール

  • brew install semgrep

pip でもインストールできるのでグローバルにインストールしたくない場合は Pipfile や pyproject.toml を使いましょう

スキャン

  • semgrep --config=auto .

初回はルールのダウンロードが走ります
以下のようなエラーが良く出ます

  • CSS のインポート時に integrity がないから XSS されます
  • パスワードや鍵などのセンシティブ情報がコード内に含まれています
  • GithubActions や docker はタグじゃなくてコミットハッシュを指定しましょう、じゃないとサプライチェーンアタックの対象になる可能性があります

などなど出てきます

ルールセット

今回は auto でしたが p/ci や p/python など使えます

https://semgrep.dev/explore

OSS 版だと使えるルールセットが限られていますが Pro 版だとよりたくさんのルールセットが使えます

Gitlab CI

https://docs.semgrep.dev/semgrep-ci/sample-ci-configs

semgrep:
  # A Docker image with Semgrep installed.
  image: semgrep/semgrep
  # Run the "semgrep ci" command on the command line of the docker image.
  script: semgrep ci

  rules:
    # Allow triggering a scan manually from the GitLab UI
    - if: $CI_PIPELINE_SOURCE == "web"
    # Scan changed files in MRs, (diff-aware scanning):
    - if: $CI_MERGE_REQUEST_IID
    # Scan mainline (default) branches and report all findings.
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

  variables:
    # Connect to Semgrep AppSec Platform through your SEMGREP_APP_TOKEN.
    # Generate a token from Semgrep AppSec Platform > Settings
    # and add it as a variable in your GitLab CI/CD project settings.
    SEMGREP_APP_TOKEN: $SEMGREP_APP_TOKEN

こんな感じらしいです
当然ですがローカルで実行してエラーをすべて解消しないと CI してもエラーになるので注意してください

最後に

semgrep を試してみました
無料版でも十分使えるレベルかなと思います
あとカスタムルールも作成できるので足りなければ自分でルールを作成すれば SAST なり SCA できます

2026年8月24日月曜日

古い認証アルゴリズムの sshd を検証で起動する方法

古い認証アルゴリズムの sshd を検証で起動する方法

概要

検証用途で一時的に起動させる方法を紹介します

環境

  • Ubuntu 24.04
  • openssh-server 9.6p1

ホスト鍵作成

mkdir -p /tmp/sshd-legacy-test
if [ ! -f /tmp/sshd-legacy-test/ssh_host_rsa_key ]; then
ssh-keygen -q -t rsa -b 2048 -f /tmp/sshd-legacy-test/ssh_host_rsa_key -N ''
fi

設定ファイル作成

cat > /tmp/sshd-legacy-test/sshd_config <<'EOF'
Port 2222
ListenAddress 127.0.0.1
HostKey /tmp/sshd-legacy-test/ssh_host_rsa_key
PidFile /tmp/sshd-legacy-test/sshd.pid
LogLevel VERBOSE
UsePAM no
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PermitRootLogin no
AllowUsers devops

X11Forwarding no
AllowTcpForwarding no
PrintMotd no
Subsystem sftp internal-sftp

KexAlgorithms +diffie-hellman-group14-sha1
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
EOF

設定ファイル確認

  • /usr/sbin/sshd -t -f /tmp/sshd-legacy-test/sshd_config

起動

  • /usr/sbin/sshd -D -e -f /tmp/sshd-legacy-test/sshd_config
  • ss -ltn | grep ':2222 '

停止

if [ -f /tmp/sshd-legacy-test/sshd.pid ]; then
kill "$(cat /tmp/sshd-legacy-test/sshd.pid)"
fi

2026年8月15日土曜日

キーチェインアクセスの GUI からパスワードが消せないときの対処方法

キーチェインアクセスの GUI からパスワードが消せないときの対処方法

概要

もうキーチェインアクセスはオワコンですが git などのコマンドは未だにキーチェインアクセスを参照しているのでキーチェインアクセス側に古いパスワードが残っている場合は削除しましょう

環境

  • macOS 26.5.2
  • git 2.55.0

コマンド

security delete-internet-password -s bitbucket.org  

bitbucket.org の部分は適宜変更してください

確認

security find-internet-password -s bitbucket.org

で security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain. になれば削除完了です

最後に

なぜか GUI からだと削除できないというなぞの現象が発生した場合は CLI を使いましょう

2026年8月3日月曜日

Stable diffusion forge Neo を試す

Stable diffusion forge Neo を試す

概要

WAI-illustrious-SDXL v17.0 が出たのでそれを試すために forge Neo が必要だったのでインストールから試してみました

環境

  • macOS 26.5.2 (m2 pro mac mini)
  • Python 3.13.14
  • StableDiffusion webui forge Neo 2.28

Python 準備

  • pyenv install 3.13.14

プロジェクトのクローン

  • git clone https://github.com/Haoming02/sd-webui-forge-classic sd-webui-forge-neo --branch neo --depth 1
  • cd sd-webui-forge-neo
  • pyenv local 3.13.14
  • pip install uv

プロジェクトのセットアップ

  • uv venv venv --python 3.13 --seed
  • vim webui-user.sh
# export TORCH_COMMAND="pip install torch==2.12.0 torchvision==0.27.0"

↓

export TORCH_COMMAND="pip install torch==2.12.0 torchvision==0.27.0"

webui 起動

  • chmod +x webui-user.sh
  • ./webui-user.sh

webui 動作確認

localhost:7860 にアクセスできれば OK です

モデルのダウンロードとインストール

https://civitai.red/models/827184/wai-illustrious-sdxl ここからダウンロードします
6.46GB ほどあります

ダウンロードしたら以下に配置します

ls -l sd-webui-forge-neo/models/Stable-diffusion/

モデルを再配置したら念のため webui を再起動しましょう

動作確認

再度 webui にアクセスしモデルが読み込まれていることを確認します
そしてプロンプトとパラメータを入力します

  • Steps: 15-30
  • CFG scale: 5-7
  • Sampler: Euler a
  • Size: 1024x1024
  • Positive Prompt: ,masterpiece,best quality,amazing quality,
  • Negative Prompt: bad quality,worst quality,worst detail,sketch,censor,

あとはプロンプトを適当に調整し「Generate」してください

最後に

macOS でも普通に動作しました

参考サイト

2026年7月22日水曜日

Aider CLI + litellm で独自のエンドポイントを使う方法

Aider CLI + litellm で独自のエンドポイントを使う方法

概要

前回 Hermes CLI + litellm を試しました
今回は Aider CLI を試してみます

なお LLM は gpt を使い設定は Hermes と同じでいけます

環境

  • Ubuntu 24.04
  • Aider 0.86.2
  • litellm 1.90.2

インストール

  • curl -LsSf https://aider.chat/install.sh | sh

litellm_config.yaml

Azure OpenAI の gpt-5.1 をモデルを使う場合は以下のようにします
モデル名は適宜変更してください

model_list:
  - model_name: chatAI
    litellm_params:
      model: azure/gpt-5.1
      api_base: https://your-custom-endpoint/gpt-5.1
      api_key: os.environ/AI_SERVICE_API_KEY
      max_tokens: 4096
      temperature: 0.2
      rpm: 30
      tpm: 60000
    model_info:
      base_model: azure/gpt-5.1

litellm_settings:
  drop_params: true

general_settings:
  global_max_parallel_requests: 1
  max_parallel_requests: 1
  max_request_size_mb: 10

router_settings:
  num_retries: 0
  retry_after: 20
  allowed_fails: 1
  cooldown_time: 180
  timeout: 120
  stream_timeout: 120

server_settings:
  port: 4000

compose.yaml

litellm を起動します

services:
  litellm:
    image: docker.litellm.ai/berriai/litellm:latest
    container_name: litellm
    environment:
      - AI_SERVICE_API_KEY=${AI_SERVICE_API_KEY}
    ports:
      - "4000:4000"
    volumes:
      - ./litellm_config.yaml:/app/config.yaml
    restart: unless-stopped
    command: ["--config", "/app/config.yaml"]

動作確認

  • export AI_SERVICE_API_KEY=secret
  • docker compose up -d

で LiteLLM を起動します

  • export OPENAI_API_BASE=http://localhost:4000/v1
  • export OPENAI_API_KEY=dummy
  • aider --model openai/chatAI

設定ファイルではなく環境変数で LiteLLM に向けます
aider が OpenAI 互換の API を使うように openai/ というプレフィックスを付与してプロバイダ名を指定します

最後に

Aider + LiteLLM の設定を紹介しました
Aider 自体が内部的に LiteLLM を使っているので簡単に呼べるようになっていました

参考サイト

2026年7月21日火曜日

Hermes CLI + litellm で独自のエンドポイントを使う方法

Hermes CLI + litellm で独自のエンドポイントを使う方法

概要

前回 gemini-cli でカスタムエンドポイントを使う方法を紹介しました
今回は Hermes CLI でカスタムエンドポイントを紹介します
ただ gemini は使えなかったので gpt を使います (前回のように Python で頑張ればできるかもです)

環境

  • Ubuntu 24.04
  • hermes cli 0.18.2
    • python 3.11.15

インストール

  • curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

~/.hermes/config.yaml

hermes cli からは litellm を使うように指定します
デフォルトだと他にも設定項目がいろいろありますが model の部分だけ以下のように修正してください
それ以外の項目はそのままで OK です

model:
  default: chatAI
  provider: custom
  base_url: http://localhost:4000/v1
  api_key: dummy

litellm_config.yaml

Azure OpenAI の gpt-5.1 をモデルを使う場合は以下のようにします
モデル名は適宜変更してください

model_list:
  - model_name: chatAI
    litellm_params:
      model: azure/gpt-5.1
      api_base: https://your-custom-endpoint/gpt-5.1
      api_key: os.environ/AI_SERVICE_API_KEY
      max_tokens: 4096
      temperature: 0.2
      rpm: 30
      tpm: 60000
    model_info:
      base_model: azure/gpt-5.1

litellm_settings:
  drop_params: true

general_settings:
  global_max_parallel_requests: 1
  max_parallel_requests: 1
  max_request_size_mb: 10

router_settings:
  num_retries: 0
  retry_after: 20
  allowed_fails: 1
  cooldown_time: 180
  timeout: 120
  stream_timeout: 120

server_settings:
  port: 4000

compose.yaml

litellm を起動します

services:
  litellm:
    image: docker.litellm.ai/berriai/litellm:latest
    container_name: litellm
    environment:
      - AI_SERVICE_API_KEY=${AI_SERVICE_API_KEY}
    ports:
      - "4000:4000"
    volumes:
      - ./litellm_config.yaml:/app/config.yaml
    restart: unless-stopped
    command: ["--config", "/app/config.yaml"]

動作確認

  • export AI_SERVICE_API_KEY=secret
  • docker compose up -d
  • hermes

最後に

Hermes CLI + litellm でカスタムエンドポイントを設定する方法を紹介しました
Hermes CLI 自体は Google AI Studio に対応しているので使えます
設定しているカスタムエンドポイント側の gemini が function calling や web_search に対応していなかったりリクエストやレスポンスの形式が少し異なる場合にはそのままでは使えません

前回のように Python なりでプロキシを立てて工夫する必要がありそうです

参考サイト

2026年7月17日金曜日

Gemini CLI + litellm で独自のエンドポイントを使う方法

Gemini CLI + litellm で独自のエンドポイントを使う方法

概要

前回 Codex 版を紹介しましたが今回は Gemini CLI での方法を紹介します

環境

  • Ubuntu 24.04
  • nodejs 22.21.0
  • gemini-cli 0.51.0

インストール

  • npm install -g @google/gemini-cli

構成概要

  • gemini-cli -> python proxy (docker) -> litellm (docker) -> custom endpoint

という流れになっています
python proxy は nginx などでも代用できるケースはありますが今回はリクエストを削る必要があるため python (fastapi) を使っています

理由は custom endpoint が gemini に完全に互換しているわけではなく使える機能により受け付けないパラメータがあるためそれを削るために用意しています
なので custom endpoint が完全互換であれば litellm だけで OK です

compose.yaml

gemini-proxy が python 製のプロキシになります
fastapi で gemini-cli から来たリクエストを受け使えないパラメータを削った後 litellm にリクエストします

services:
  litellm:
    image: docker.litellm.ai/berriai/litellm:latest
    container_name: litellm
    environment:
      - AI_SERVICE_API_KEY=${AI_SERVICE_API_KEY}
    ports:
      - "4000:4000"
    volumes:
      - ./litellm_config.yaml:/app/config.yaml
    restart: unless-stopped
    command: ["--config", "/app/config.yaml"]

  gemini-proxy:
    # Python proxy for gemini CLI (native Gemini API format).
    # Replaces nginx because request body JSON transformation is required:
    # strips unsupported 'id' field from function_call/function_response,
    # rewrites :streamGenerateContent?alt=sse → :streamGenerateContentSse,
    # and injects AI_SERVICE_API_KEY as x-goog-api-key header.
    # Configure gemini CLI to use port 4001 instead of 4000.
    build:
      context: .
      dockerfile: Dockerfile.proxy
    container_name: gemini-proxy
    environment:
      - AI_SERVICE_API_KEY=${AI_SERVICE_API_KEY}
    ports:
      - "4001:4001"
    restart: unless-stopped

proxy.py

API を互換させるためのプロキシです
先ほども紹介しましたが custom endpoint が完全互換の場合は不要です

#!/usr/bin/env python3
"""
Proxy for gemini CLI → Your custom Gemini endpoint.

Handles:
- URL mapping: :streamGenerateContent?alt=sse → :streamGenerateContentSse
- Request body: strip unsupported 'id' field from function_call / function_response
- Auth: replace incoming key with AI_SERVICE_API_KEY as x-goog-api-key header
"""

import json
import os

import httpx
from fastapi import FastAPI, Request
from fastapi.responses import Response, StreamingResponse

CUSTOM_BASE = os.environ.get(
    "CUSTOM_BASE",
    "https://your-gemini-custom-endpoint",
)
API_KEY = os.environ["AI_SERVICE_API_KEY"]

app = FastAPI()


def strip_unsupported_fields(body: dict) -> dict:
    """Strip 'id' from function_call/function_response — field not supported by this endpoint."""
    for content in body.get("contents", []):
        for part in content.get("parts", []):
            for key in ("functionCall", "function_call"):
                if isinstance(part.get(key), dict):
                    part[key].pop("id", None)
            for key in ("functionResponse", "function_response"):
                if isinstance(part.get(key), dict):
                    part[key].pop("id", None)
    return body


@app.post("/v1beta/models/{model}:{method}")
async def proxy(request: Request, model: str, method: str):
    is_streaming = method == "streamGenerateContent"
    # Custom endpoint uses a dedicated SSE endpoint instead of ?alt=sse query param
    target_method = "streamGenerateContentSse" if is_streaming else method
    target_url = f"{CUSTOM_BASE}/{model}:{target_method}"

    body_bytes = await request.body()
    try:
        body = strip_unsupported_fields(json.loads(body_bytes))
        body_bytes = json.dumps(body, ensure_ascii=False).encode()
    except (json.JSONDecodeError, AttributeError):
        pass

    upstream_headers = {"Content-Type": "application/json", "x-goog-api-key": API_KEY}

    if is_streaming:

        async def generate():
            async with httpx.AsyncClient(timeout=120.0) as client:
                async with client.stream(
                    "POST", target_url, content=body_bytes, headers=upstream_headers
                ) as resp:
                    async for chunk in resp.aiter_bytes():
                        yield chunk

        return StreamingResponse(
            generate(),
            media_type="text/event-stream",
            headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
        )
    else:
        async with httpx.AsyncClient(timeout=120.0) as client:
            resp = await client.post(
                target_url, content=body_bytes, headers=upstream_headers
            )
        return Response(
            content=resp.content,
            status_code=resp.status_code,
            media_type=resp.headers.get("content-type", "application/json"),
        )

Dockerfile.proxy

先程の proxy.py をビルドしてイメージを作成する dockerfile です

FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir fastapi httpx uvicorn
COPY proxy.py .
CMD ["uvicorn", "proxy:app", "--host", "0.0.0.0", "--port", "4001"]

litellm_config.yaml

gemini-3.1-flash-lite 側は基本不要ですが gemini-cli がデフォルトで gemini-3.1-flash-lite のモデルの定義を探しに行くのでエラーにならないように追加しています
pass_through_endpoints でパスのオーバライドをしていますがこれも状況によるので必須ではないです

model_list:
  - model_name: gemini-3.5-flash
    litellm_params:
      # gemini/ prefix is required to use API key auth instead of Vertex AI ADC.
      # Model name must match model_name above to avoid LiteLLM response model override error
      # that corrupts streaming responses. drop_params: true handles unsupported fields.
      model: gemini/gemini-3.5-flash
      api_base: https://your-gemini-custom-endpoint:generateContent
      api_key: os.environ/AI_SERVICE_API_KEY
      max_tokens: 4096
      temperature: 0.2
      rpm: 30
      tpm: 60000
  - model_name: gemini-3.1-flash-lite
    litellm_params:
      # gemini/ prefix is required to use API key auth instead of Vertex AI ADC.
      model: gemini/gemini-3.5-flash
      api_base: https://your-gemini-custom-endpoint:generateContent
      api_key: os.environ/AI_SERVICE_API_KEY

litellm_settings:
  drop_params: true

general_settings:
  global_max_parallel_requests: 1
  max_parallel_requests: 1
  # Pass-through: forward native Gemini API requests (/v1beta/models/...) directly
  # to the upstream endpoint, bypassing LiteLLM's model routing and SDK layer.
  # This avoids the "cannot override response model" bug caused by GenerateContentResponse
  # not having a 'model' attribute in LiteLLM's google_genai provider path.
  pass_through_endpoints:
    - path: "/v1beta/models/{path:path}"
      target: "https://your-gemini-custom-endpoint/gemini/{path}"
      headers:
        x-goog-api-key: os.environ/AI_SERVICE_API_KEY

router_settings:
  num_retries: 0
  retry_after: 20
  allowed_fails: 1
  cooldown_time: 180
  timeout: 120
  stream_timeout: 120

server_settings:
  port: 4000

動作確認

  • docker compose up -d
  • export GOOGLE_GEMINI_BASE_URL="http://localhost:4001"
  • gemini --model=gemini-3.5-flash

で「hi」とか打ってレスポンスが返ってくればとりあえず接続はできています

最後に

gemini-cli + litellm + custom endpoint の設定方法を紹介しました

独自のエンドポイントが提供している gemini などがあり API が完全互換していない場合は自分でリクエストやレスポンスを細工する必要があります
今回は fastapi + litellm で対応しましたが方法は様々なので自分に合う方法で互換させれば OK です

基本はエラーに合わせてプロキシや litellm 側で調整するしかないです

{
  "error": {
    "code": 400,
    "message": "Invalid JSON payload received. Unknown name \"id\" at 'contents[2].parts[0].function_call': Cannot find field.\nInvalid JSON payload received. Unknown name \"id\" at 'contents[3].parts[0].function_response': Cannot find field.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "contents[2].parts[0].function_call",
            "description": "Invalid JSON payload received. Unknown name \"id\" at 'contents[2].parts[0].function_call': Cannot find field."
          },
          {
            "field": "contents[3].parts[0].function_response",
            "description": "Invalid JSON payload received. Unknown name \"id\" at 'contents[3].parts[0].function_response': Cannot find field."
          }
        ]
      }
    ]
  }
}

参考サイト

2026年7月8日水曜日

Ubuntu glab にコマンドをインストールするスクリプト

Ubuntu glab にコマンドをインストールするスクリプト

概要

シェルスクリプトを紹介します

環境

  • Ubuntu 24.04
  • glab 1.106.0

スクリプト

  • vim install_glab_on_ubuntu.sh
set -euo pipefail

sudo apt-get update
sudo apt-get install -y curl tar jq

ARCH_RAW="$(uname -m)"
case "$ARCH_RAW" in
  x86_64) ARCH_SUFFIX="amd64" ;;
  aarch64|arm64) ARCH_SUFFIX="arm64" ;;
  *)
    echo "Unsupported architecture: $ARCH_RAW" >&2
    exit 1
    ;;
esac

API_URL="https://gitlab.com/api/v4/projects/gitlab-org%2Fcli/releases/permalink/latest"
RELEASE_JSON="$(curl -fsSL "$API_URL")"
TAG_NAME="$(echo "$RELEASE_JSON" | jq -r '.tag_name')"

# ここが重要: 現在の命名は glab_<ver>_linux_<arch>.tar.gz
DOWNLOAD_URL="$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH_SUFFIX" '
  .assets.links[]
  | select((.name // "") | test("_linux_" + $arch + "\\.tar\\.gz$"))
  | (.direct_asset_url // .url)
' | head -n1)"

if [[ -z "$DOWNLOAD_URL" || "$DOWNLOAD_URL" == "null" ]]; then
  echo "No matching asset URL found for arch: $ARCH_SUFFIX" >&2
  echo "DEBUG: available asset names:" >&2
  echo "$RELEASE_JSON" | jq -r '.assets.links[].name' >&2
  exit 1
fi

TMP_DIR="/tmp/glab-install"
TARBALL_PATH="$TMP_DIR/glab.tar.gz"

echo "DEBUG: ARCH_RAW=$ARCH_RAW"
echo "DEBUG: ARCH_SUFFIX=$ARCH_SUFFIX"
echo "DEBUG: API_URL=$API_URL"
echo "DEBUG: TAG_NAME=$TAG_NAME"
echo "DEBUG: DOWNLOAD_URL=$DOWNLOAD_URL"
echo "DEBUG: TMP_DIR=$TMP_DIR"
echo "DEBUG: TARBALL_PATH=$TARBALL_PATH"

rm -rf "$TMP_DIR"
mkdir -p "$TMP_DIR"

curl -fL "$DOWNLOAD_URL" -o "$TARBALL_PATH"
tar -xzf "$TARBALL_PATH" -C "$TMP_DIR"

GLAB_BIN="$(find "$TMP_DIR" -type f -name glab | head -n1)"
if [[ -z "$GLAB_BIN" ]]; then
  echo "glab binary not found in extracted archive" >&2
  exit 1
fi

sudo install -m 0755 "$GLAB_BIN" /usr/local/bin/glab
glab --version

解説

いろいろやっていますが https://gitlab.com/gitlab-org/cli/-/releases ここから tar をダウンロードし PATH 上に配置しているだけです

最後に

あとは glab auth でログインしましょう

export GITLAB_TOKEN='glpat-c-xxx'
glab auth login --hostname gitlab.devops.nifcloud.net --token <<< "$GITLAB_TOKEN"
glab auth status --hostname gitlab.devops.nifcloud.net

2026年7月7日火曜日

code-server でファイルを開く際に必ず新しいタブで開くようにする方法

code-server でファイルを開く際に必ず新しいタブで開くようにする方法

概要

タイトルの通り

環境

  • code-server 4.126.0

対策方法

"workbench.editor.enablePreview": false

2026年7月6日月曜日

Mac で Ctrl+g で勝手に Gemini が起動するのを止める方法

Mac で Ctrl+g で勝手に Gemini が起動するのを止める方法

概要

Chrome のショートカットキーで勝手に割り当てられるようになってしまったようです

環境

  • macOS 26.5.1
  • Chrome 149.0.7827.201

方法

  1. chrome://settings/ai/gemini を開く
  2. キーボードショートカットで Gemini を開きますのバツボタンを押す

これが

こうなれば OK です

最後に

たぶんそのうちデフォルトで無効になるはずです

参考サイト

2026年7月4日土曜日

codex を Ubuntu で動かす場合のサンドボックス設定

codex を Ubuntu で動かす場合のサンドボックス設定

概要

自動化する場合には書き込めるように codex がサンドボックスを作成できるようにしてあげる必要があります

環境

  • Ubuntu 24.04
  • codex 0.142.5

主なエラー

Unfortunately, I **cannot execute any commands** in this environment — the sandbox is rejecting all shell execution with:

bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted


This means I cannot:
- Read the actual file contents
- Apply patches
- Run tests

Could you check if there's a sandbox configuration issue? Once commands can run, I can implement the fix directly. Alternatively, if you can share the contents of the two files, I can produce the exact patch needed.
Codex's Linux sandbox uses bubblewrap and needs access to create user namespaces.

対処方法

  • sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

以下で確認し 0 になっていれば OK です

  • sudo sysctl kernel.apparmor_restrict_unprivileged_userns
0

インスタンスの再起動などは不要です

最後に

danger-full-access は可能な限りやめましょう
名前的にもやばいオプション名なので

参考サイト

2026年7月3日金曜日

Codex + LiteLLM + カスタムOpenAIエンドポイントの設定方法

Codex + LiteLLM + カスタムOpenAIエンドポイントの設定方法

概要

素の OpenAI エンドポイントではなく特定の環境でラップされている URL の場合には LiteLLM を挟みましょう もしくは API が完全非互換でなかったり https プロトコルのみ提供されている場合なども LiteLLM を挟むと解決することがあります

環境

  • Ubuntu 24.04
  • codex 0.142.5
  • LiteLLM 1.90.0

LiteLLM 設定

api_base と api_key に自身の環境のカスタムOpenAIエンドポイントとカギを設定しましょう

  • vim litellm/litellm_config.yaml
model_list:
  - model_name: claude-chatAI
    litellm_params:
      model: anthropic/claude-sonnet-4-6
      api_base: https://your-api-endpoint
      api_key: xxx
      max_tokens: 4096
      temperature: 0.2

server_settings:
  port: 4000
  • vim litellm/compose.yaml
services:
  litellm:
    image: docker.litellm.ai/berriai/litellm:latest
    container_name: litellm
    ports:
      - "4000:4000"
    volumes:
      - ./litellm_config.yaml:/app/config.yaml
    restart: unless-stopped
    command: ["--config", "/app/config.yaml"]
  • docker compose up -d

codex 設定

base_url は LiteLLM が Listen しているアドレスで env_key は下記の SONNET_API_KEY を指定します
LiteLLM 側で認証情報を指定していますが一応 codex 側でも鍵情報を指定するためです

  • vim ~/.codex/litellm.config.toml
model_provider = "litellm"
model = "anthropic/claude-sonnet-4-6"
web_search = "disabled"

[model_providers.litellm]
name = "sonnet"
base_url = "http://localhost:4000"
env_key = "SONNET_API_KEY"
wire_api = "responses"

動作確認

  • export SONNET_API_KEY="xxx"
  • codex --profile litellm

これでインタラクティブモードで起動するので「test」など投げて応答がくれば OK です

最後に

たぶん OpenInterpreter も同じ方法でいけます
--full-auto オプションなどは廃止になっているの注意しましょう

参考サイト

https://docs.litellm.ai/docs/tutorials/openai_codex