OmniAICreator/Qiita-1.07M huggingface dataset:
個人的備忘録：シンプルだけど奥深いBasic認証と、その他の認証方式を体系的に整理してみた

## はじめに

WebアプリケーションやAPIを公開する際、認証はセキュリティ上非常に重要な仕組みです。本記事では、最も基本的な認証方式である"Basic認証"について解説し、その仕組みや使用方法、注意点を中心に取り上げます。

:::note info
**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**
:::

あわせて、Basic認証以外の代表的な認証方式についても簡単にまとめます。

## Basic認証とは

Basic認証は、HTTPの標準仕様に基づいた最もシンプルな認証方式の一つです。ユーザー名とパスワードをBase64でエンコードしてHTTPヘッダーに含め、サーバーがその情報を元に認証します。

**参考文献**

https://e-words.jp/w/Basic%E8%AA%8D%E8%A8%BC.html#:~:text=Basic%E8%AA%8D%E8%A8%BC%EF%BC%88%E3%83%99%E3%83%BC%E3%82%B7%E3%83%83%E3%82%AF%E8%AA%8D%E8%A8%BC%EF%BC%89%E3%81%A8,%E5%8D%98%E7%B4%94%E3%81%AA%E4%BB%95%E6%A7%98%E3%81%AE%E3%82%82%E3%81%AE%E3%80%82

### 特徴
- 実装が簡単
- 一般的なブラウザやツールが対応
- 通信が暗号化されていない場合、セキュリティリスクが高い（HTTPS併用推奨）

### 仕組み（流れ）
1. クライアントがリクエストを送信
2. サーバーが`401 Unauthorized`と`WWW-Authenticate`ヘッダーを返す
3. クライアントがユーザー名とパスワードをBase64エンコードして再送信
4. サーバーが認証し、正しければレスポンスを返す

### 利用例（Apache）
```apache
# .htaccess
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /path/to/.htpasswd
Require valid-user
```

## Basic認証の注意点
- Base64は暗号化ではなくエンコードであるため、平文に戻せる
- 認証情報が毎回送信されるため、盗聴リスクがある
- 必ずHTTPSと組み合わせることが推奨される

## その他の認証方式

### 1. Digest認証
Basic認証よりも安全性を高めた方式で、ハッシュ化された情報を用いて認証する。

- ユーザー情報が平文で送信されない
- ただし、現在ではモダンなAPIにはほとんど使われない

**参考文献**

https://e-words.jp/w/Digest%E8%AA%8D%E8%A8%BC.html#:~:text=Digest%E8%AA%8D%E8%A8%BC%EF%BC%88digest%20authentication%EF%BC%89%E3%81%A8,%E3%82%92%E9%98%B2%E3%81%90%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B%E3%80%82

### 2. トークンベース認証（例：JWT）
JSON Web Token（JWT）を使って、クライアントが一度ログインした後、トークンを用いて認証を行う。

- ステートレス
- クライアントサイドでトークンを保持し、ヘッダーに付加してリクエスト送信

**参考文献**

https://www.okta.com/jp/identity-101/what-is-token-based-authentication/

### 3. OAuth 2.0
外部サービスとの連携や権限委譲を行う際に使われる標準的なフレームワーク。

- スコープによるアクセス制限が可能
- Webアプリやモバイルアプリでよく使われる
- 実装が複雑な反面、柔軟性が高い

**参考文献**

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be

### 4. セッション認証（Cookieベース）
ログイン時にセッションIDを発行し、ブラウザのCookieに保持する方式。

- Webアプリで古くから使われている
- クロスサイトスクリプティング（XSS）対策が重要

**参考文献**

https://qiita.com/hisashi_matsui/items/3dec3f90ab5cf1026960

### 5. LDAP認証
LDAP（Lightweight Directory Access Protocol）を使用して、社内のディレクトリサービス（Active Directoryなど）と連携してユーザー認証を行う方式。

- 主に企業内の統合認証で利用される
- 一元管理されたユーザー情報を利用可能
- 通常はStartTLSまたはLDAPSで通信を暗号化
- システムやネットワークの設定が複雑になることがある

**参考文献**

https://www.onelogin.com/jp-ja/learn/what-is-ldap#:~:text=LDAP%E8%AA%8D%E8%A8%BC%E3%81%A8%E3%81%AF%E4%BD%95,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82

## まとめ

Basic認証は導入のしやすさから、開発・テスト環境などでよく使われるシンプルな認証方式です。

:::note alert
**しかし、本番環境で使用する際は、セキュリティリスクへの十分な配慮が必要です。**
:::

より安全性の高いJWTやOAuth 2.0、LDAPなど、利用目的に応じた認証方式の選定が求められます。

---
※本記事は、個人の備忘録としてまとめています。実際の導入の際は、公式ドキュメント等もご確認ください。
[備忘録] Docker

## 起動オプション
- d デーモン化する。これつけないと1回実行で終わる
- i 標準入力（stdin）を維持する
- t 疑似ターミナルを割り当てる
実行例
```
docker run -dit ubuntu bash

#後でログイン
docker exec -it ubuntu bash

```

## ボリュームのマウント
### ボリュームマウント
Docker Engine管理のボリュームをマウントする
```
docker volume create hoge
docker volume rm hoge

#オプション指定
-v hoge:コンテナ内のパス
```

### バインドマウント
ホスト側のディレクトリやファイルをマウントする
```
#オプション指定
-v ホスト側のパス:コンテナ内のパス
```
LLMがチームを組む時代。Google A2Aを動かしてみた

長いものには巻かれろ理論で、流行りのGoogle A2Aを触ってみました。
https://cloud.google.com/blog/ja/products/ai-machine-learning/a2a-a-new-era-of-agent-interoperability

# まず、A2Aって？？
GoogleのAgent2Agent（A2A）は、異なるAIエージェント同士が協力し合ってタスクをこなすための通信プロトコル。
例えば、カスタマーサポートのAIが、社内の在庫管理AIと連携して「在庫あります」と即答できるようになるような仕組みや、旅行代理店のAIが航空券AIと宿泊予約AIに横断的に問い合わせて「最安のツアー」を即時に提案するとか！

昨今よく利用されているModel Context Protocol（MCP）は、AIが外部のツールや情報ソースなどの他システムと連携する仕組みであるのに対して、A2Aではエージェント同士が協働する仕組みであることが特徴。

# でも手を動かさんとよくわからん！！
ということで、Googleのデモを触ってみました。

## 1️⃣ こちらのリポジトリからClone
https://github.com/google/A2A

## 2️⃣ リポジトリの、Sampleディレクトリに移動
```
cd A2A/samples/
```

## 3️⃣ 仮想環境の作成と有効化
```
uv venv
source .venv/bin/activate
```

## 4️⃣ 環境変数のセット
```
export GOOGLE_API_KEY={your api key}
```

## 5️⃣ 各エージェントの起動
#### 📊 画像を生成できるサンプルエージェント
```
cd A2A/samples/python/agents/crewai
uv run . --host 0.0.0.0 --port 10000
```
#### 🤖 経費報告書の記入を（模擬的に）行うサンプルエージェント
```
cd A2A/samples/python/agents/google_adk
uv run . --host 0.0.0.0 --port 10001
```
#### 💰 通貨を換算できるサンプルエージェント
```
cd A2A/samples/python/agents/langgraph
uv run . --host 0.0.0.0 --port 10002
```

## 6️⃣ demo GUIの起動
```
cd A2A/demo/ui
uv run main.py
```

## 7️⃣ UI上で起動したエージェントを登録
Agentsタブを選択して、左下のボタンから追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3722068/245998ff-a4c5-49a6-87f9-58bf2e3fc9bd.png)

このように、IPとPORTのみ記載します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3722068/5bb6b8d1-9006-4daf-ad2a-01c4c5008eaa.png)

## 8️⃣ Homeタブから Conversationに入り、会話を楽しんでください！
「他にお手伝いできることはありますか？（圧）」
ちょっとtemperture低めで冷たいですねww

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3722068/b7e3663d-6cb4-4602-8e4a-c343bc721bf3.png)

# 少しいじってみて
Agent同士の連携プロトコルっていうのは、とても新しい発想だなと思いました。

LangChainや、LangMemと連携して、応答エージェントとして使った上で、専門的な内容を処理するエージェントと連携できれば、応答精度が結構上がりそうだな...。（ニヤリ）としております。

少しMCPにも手を伸ばしてみて、それぞれの良し悪しも詳しく見てみたいなぁと思ってます。

# この記事を作成するにあたって
こちらの記事を参考にさせていただきました！ありがとうございます。
https://zenn.dev/he/articles/fd63e6b229e661

# さいごに
気に入ってもらえたら、いいねやフォローお待ちしてます！
競プロ日記#25/04/11

# アルゴ式-[積ん読](https://algo-method.com/tasks/b0d53a6d2ea991ed)
- 今回の問題は残っている本を答えるのではなくて読んだ本（消化した本）を答えるのでstackを使って追加時はpush、読み終え時はtopでアクセス→popで削除で良い
- がしかし結局vectorで上手くやる方が柔軟性があって良い
```
#include <bits/stdc++.h>
#include <algorithm>
#include <stack>
using namespace std;

using Graph = vector<vector<int>>;

int main() {
    int Q;
    cin >> Q;
    vector<string> S;

    for (int i = 0;i < Q;i++){
        int n;
        cin >> n;
        if(n == 1){
            string s;
            cin >> s;
            S.push_back(s);
        }else{
            cout << S.back() << endl;
            S.pop_back();
        }
    }
    
    
}
```
(Adafruit_NeoPixel)XIAO ESP32C6、RGB LEDで遊ぶ

いろいろ、注意
----


- 過去ログをみょ!!!
- Adafruit NeoPixelをインストール


目的
----

M5Stack社製のGROVEシステム RGB LEDを光らせて遊ぶ


結果
----

![image_original (86).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/759cef47-a0bf-4779-92d6-ef3973f9f9b4.jpeg)


プログラム
----

```c++



//rgb_led_XIAO_ESP32C6_2


//インクルド
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>


//定義
#define NUM_LEDS 3
#define OUT_GPIO_NUM D10 //XIAO-ESP32C6
Adafruit_NeoPixel strip(NUM_LEDS, OUT_GPIO_NUM, NEO_GRB + NEO_KHZ800);


//初期化
void setup() {
  // put your setup code here, to run once:

  //ネオピクセルの設定
  strip.begin();
  strip.show();

  //カラーセット
  strip.setPixelColor(0, strip.Color( 5, 0, 0));
  strip.setPixelColor(1, strip.Color( 0, 5, 0));
  strip.setPixelColor(2, strip.Color( 0, 0, 5));
  
  strip.show();//再表示

}//setup


//メインループ
void loop() {
  // put your main code here, to run repeatedly:

  delay(3); //ダミー

}//loop



```
ASP.NET Core + Docker環境をDocker CLI + Limaで動かす初期設定

プライベートのmacにてDocker Desktopの利用をやめ，Docker CLI + Lima環境に移行しました．

その際にASP.NET Core + Dockerで構築したプロジェクトが動作するか試してみたのですが，デフォルトのLimaではASP.NET Coreコンテナのビルド時にエラーが起こってしまいました．

当記事では発生した問題と，その解決方法について簡単にまとめます．

## 環境

- macOS Sequoia 15.4
- Lima version 1.0.7

## 発生した問題

単に`limactl start`コマンドで起動したデフォルト設定のLimaで`docker compose up -d`を試みたところ，以下のようなエラーが発生しました．

```
/usr/share/dotnet/sdk/8.0.xxx/NuGet.targets(174,5): error : Read-only file system : '/app/src/obj/2134170y-3021-3548-8271-2e83u6e22s8ba.tmp'
```

ASP.NET Coreのビルドの際，objディレクトリにファイルを生成するようですが，Limaのインスタンスからではその書き込み権限がなく，エラーとなっている様子です．

## 解決方法（Limaの初期設定）
そこで以下の手順でLimaの環境を構築したところ，無事動作するようになりました．

### 1. Limaのインスタンス作成

以下のコマンドを実行して，インスタンスを作成します．

```sh
limactl create --name=[インスタンス名] template://docker
```

コマンド実行後にいくつか操作の選択肢を聞かれるので，`Open an editor to review or modify the current configuration`を選択します．

ローカルの`~/.lima/[インスタンス名]/lima.yaml`が開くので．`mounts:`の箇所にプロジェクトファイルのパスを記載します．

```lima.yaml
mounts:
  - location: "~/your/project/path" # 追加
    writable: true
```

これによりLimaから指定パス以下のファイルへの書き込みが可能となります．

上記w追記後，以下のコマンドによりインスタンスを起動します．

```sh
limactl start [インスタンス名]
```

### 2. .NET SDKのインストール

.NET SDKをコンテナでも利用したい場合は，そのインストールが必要です．
`lima shell [インスタンス名]`コマンドでインスタンスのシェルを開き，以下のコマンドで.NET SDK（ここでは.NET 8系）をインストールします．

```sh
sudo apt update && sudo apt -y install dotnet-sdk-8.0
```

### 3. 設定完了

これにてLimaの初期設定は完了となります．

あとは`lima docker compose up -d`コマンドをローカルで実行，あるいはLimaのシェル内で`docker compose up -d`する等でコンテナを立てれば，プロジェクトが動作するようになります！
【LangSmith × Cloud Run】トレースが動かない!? → 実は「動いてた」話（3時間溶けました）

## 😇 TL;DR（最初に結論）

**LangSmith でのトレースは、`LANGSMITH_TRACING=true` が環境変数に設定されているだけで有効になります。**  
→ `tracing_v2_enabled()` 等をコードに書かなくても、自動で記録されるようになってました！

## 🧪 環境構成

- FastAPI（Python 3.11）
- LangGraph / LangMem
- LangChain + LangSmith
- Cloud Run（Dockerデプロイ）
- PostgreSQL（AsyncPostgresStore）
- CloudRun 環境変数にて `LANGSMITH_*` を設定

```env
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=（略）
LANGSMITH_API_KEY=（略）
LANGSMITH_PROJECT=（略）
```

## 🧩 やりたかったこと

LangSmith を使って、  
Cloud Run 上で動いているエージェントの挙動（プロンプト最適化含む）をトレースしたい！

## 💥 ハマったポイント

1. `tracing_v2_enabled()` が必要だと思って、`run_agent()` を書き換え
2. `from langchain_core.tracers import tracing_v2_enabled` が ImportError に（←バージョン最新なのに）
3. langchain-core のバージョン固定して入れ直す → ビルド失敗
4. Dockerfile を微調整 → 何度も Cloud Run でデプロイ失敗
5. PORT 8080 のエラーに騙されてヘルスチェックなどを疑う

## 🔍 原因はまさかの「勝手に有効になってた」

実は、LangSmith のトレース機能は **環境変数があれば自動で発火します。**

```env
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=（略）
LANGSMITH_API_KEY=（略）
LANGSMITH_PROJECT=（略）
```

↑ これがあれば、LangChain SDK 側で自動的にフックされ、LangSmith に送信されます。  
コードに `tracing_v2_enabled()` を書かなくても、**勝手に**記録されてる。


## 🧠 気づいたきっかけ

LangSmith に何も連携してない状態に**切り戻した**のに、LangSmith の GUI にログが上がってきていた。

「は？？？なんで？？？」

→ 環境変数がそのままだったことに気づき、真実が判明。


## ✅ 教訓まとめ

| ハマりポイント | 教訓 |
|----------------|------|
| tracing_v2_enabled() を書いたのに ImportError | そもそも書かなくていい |
| Cloud Run で PORT エラーが出る | 起動に失敗してる原因はコード側（ImportErrorなど）かも |
| LangChain のバージョンを調整しまくる | LangSmith のトレースには関係ない |
| トレースされない！と思い込んでコードを大幅変更 | 環境変数をまず疑うべし |

## 🧘‍♂️ 結果と感想

3時間溶けたけど、  
LangSmith が **かなり優秀なトレース機能を持ってる**ことが体験でよく分かった。
こんな感じで推論の流れを全て見れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3722068/0cd96479-d222-46b4-b215-2fed681e3b52.png)


パラメータの最適化にもかなり使えるので、LangChain + Cloud Run な構成では  
**開発時は `LANGSMITH_TRACING=true` にしておくのをオススメします。**
※本番環境では、プライバシーの問題とかもあるので慎重に...。

## 💤 最後に

```bash
$ echo "このQiita記事は、僕のHP -1 からの復活ログである"
$ echo "少しでも誰かの時間とHPを救えたら幸いです🙏"
```
🌱 Laravel Collectionの map() と each()のはなし〜


Laravelでデータを整形する際、map() や each()、filter()、pluck() など、さまざまなCollectionメソッドを使います。

その中でも特に混乱しやすいのが、map() と each() です。

「なんとなく似てるけど、どう違うの？」

「filter() のあとに values() って付けるべき？」

「配列と Collection、どこで使い分けるのが良い？」

こうした疑問を感じたことはありませんか？

この記事では、

map() と each() の基本的な違い

よくある使い間違い

実務で役立つCollection活用例（map+tap、filter→values など）

をわかりやすく整理します。

## ✅ このブログでわかること
map() と each() の基本的な違い

よくある使い間違いとバグパターン

実務でよく使うCollection活用例（map＋tap、filter→values など）

### 📘 本編はこちら
👉 Laravel Collection入門：mapとeachの違い、ちゃんと説明できますか？
🔗 https://mikaduki.info/laravel/collection-map-vs-each/

## 🙇 最後にちょっとだけ…
Qiitaでは軽めの紹介にしてるけど、
コード例・やらかしポイント・実務でよくあるパターンとか、
本編にはもっと詳しく書いてあるよ〜！
TanStack Routerで画面遷移時にデータを渡す


## 概要
[TanStack Router](https://tanstack.com/router/latest/docs/framework/react/overview)は画面遷移時に **任意の状態(state)** を添えて画面遷移することができます。（クエリパラメータを添付する方法については触れません。）

ただし、TypeScriptで開発していると、[公式ドキュメントのサンプルコード](https://tanstack.com/router/latest/docs/framework/react/api/router/useNavigateHook#examples)をそのまま使うと型エラーが発生したため、本記事では対処方法を解説します。


## 開発環境
- TypeScript
- TanStack Router v1
- React + Vite


## 発生した型エラー

以下のように`navigate`の`state`に値を渡したとき、TypeScriptの型エラーが発生

```tsx
navigate({
  to: '/groups/$groupId/reports',
  params: { groupId: report.groupId },
  state: {
    userId: "sample",
  },
});
```

### エラー内容
`state`に渡すプロパティが型として未定義なため発生するエラーです。
```
オブジェクト リテラルは既知のプロパティのみ指定できます。'userId' は型 'NonNullableUpdater<HistoryState>' に存在しません。ts(2353)
```

## 解決方法

TypeScriptで型安全に`state`を使うためには、型を拡張してやる必要があります。具体的には`@tanstack/react-router`モジュールを拡張し、`HistoryState`インターフェースに値を定義してやります。こうすることで、上記の型エラーは消えるはずです。

```tsx
// main.tsxなど、routerインスタンスを定義しているファイル

import { createRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree';

export const router = createRouter({ routeTree });

// 型拡張。TS環境ならすでにこちらの記述は存在するはず
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }

  // 以下を追加。stateに渡せるプロパティを明示的に定義
  interface HistoryState {
    userId?: string;
  }
}
```


## 実装例

簡単な実装例を示します。

#### 遷移元：stateを渡して画面遷移

```tsx
import { useNavigate } from '@tanstack/react-router';

export const UserList = () => {
  const navigate = useNavigate();

  const handleNavigate = () => {
    navigate({
      to: '/users/$userId',
      params: { userId: '123' },
      state: {
        userId: '123',
      },
    });
  };

  return <button onClick={handleNavigate}>詳細へ</button>;
};
```

#### 遷移先：stateを受け取る

[useRouterState Hook](https://tanstack.com/router/latest/docs/framework/react/api/router/useRouterStateHook)を使用して`state`を受け取る。

```tsx
import { useRouterState } from '@tanstack/react-router';

export const UserDetail = () => {
  const userId = useRouterState({
    select: (state) => state.location.state.userId,
  });

  return (
    <div>
      <h1>ユーザー詳細</h1>
      {userId && <p>ユーザーID: {state.userId}</p>}
    </div>
  );
};
```

## まとめ
- `navigate({ state })`で一時的な状態を画面遷移に添えられる
- TypeScriptで使う場合は、`HistoryState` を `declare module` で拡張する必要がある
- `useRouterState` を使うと、stateを安全に受け取れる

サーチパラメータでURLを汚さずに表示制御や一時的な情報共有が可能になります。参考になれば幸いです。

## 参考リンク

https://tanstack.com/router/latest/docs/framework/react/overview

https://github.com/TanStack/router/discussions/284
Elixir miseでインストール

```bash
#!/bin/bash

# Execute this script current shell.
# $ source ./install_mise.sh

# mise
# https://mise.jdx.dev/getting-started.html
# https://github.com/jdx/mise?tab=readme-ov-file

# If you use Linux, install mise.
# After install mise, add path in current shell
_linux_install_mise() {
  echo "----- Linux install mise."
  curl https://mise.run | sh

  ~/.local/bin/mise --version

  echo "----- Add path to ~/.bashrc."

  # .bashrc
  echo '# mise' >> ~/.bashrc
  echo "eval \"\$(/home/pi/.local/bin/mise activate bash)\"" >> ~/.bashrc

  source ~/.bashrc
  which mise
}

# If you use macOS, execute `brew install mise`.
# After install mise, add path in current shell
_macOS_install_mise() {
  echo "----- macOS install mise."
  brew update
  brew install mise

  echo "----- Add path to ~/.zshrc."

  # .zshrc
  echo '# mise' >> ${ZDOTDIR:-~}/.zshrc
  echo 'eval "$(mise activate zsh)"' >> ${ZDOTDIR:-~}/.zshrc

  source ${ZDOTDIR:-~}/.zshrc
  which mise
}

install_mise() {
  if [ "$(uname)" = 'Darwin' ]; then
    _macOS_install_mise
  else
    _linux_install_mise
  fi
}

install_mise
```

```bash
#!/bin/bash

# Install version
ELIXIR_V=1.18.3
ERLANG_V=27.3.2

# Update mise.
# If you use macOS, execute `brew upgrade mise`.
# If you use Linux, execute `mise self-update`.
update_mise() {
  if [ "$(uname)" = 'Darwin' ]; then
    echo "----- macOS update brew mise."
    brew update
    brew upgrade mise
  else
    echo "----- Linux update mise."
    mise self-update
  fi
}

# List installable elixir and eralng versions.
version_list() {
  mise ls-remote erlang | tail
  mise ls-remote elixir | tail
}

# Uninstall elixir and erlang current setting versions.
uninstall() {

  echo "----- Uninstall Erlang."
  mise uninstall erlang

  echo "----- Uninstal Elixir."
  mise uninstall elixir
}

# Install specified elixir and erlang version.
# And set global installed version.
install_elixir() {

  echo "----- Install Erlang."
  mise use --global erlang@${ERLANG_V}

  echo "----- Install Elixir."
  mise use --global elixir@${ELIXIR_V}

  mise ls
}

# version_list
update_mise
uninstall
install_elixir
```
【Rails】データ保存について

# 記事概要
Ruby on Railsのデータ保存について、まとめる

# 前提
- Ruby on Railsでアプリケーションを作成している

# まとめ
## 手順(Sequel Ace)
### 起動
1. [Sequel AceでDBを確認](https://qiita.com/likapantrip/items/4c363d844847825d2dca)する
1. テーブルを選択する

### 新規作成
1. データが保存されていないレコード上で、ダブルクリックする
1. データを入力するカラムをダブルクリックする
1. 必須項目にデータを入力する
1. エンターボタンを押すと、入力データが保存される
    [![Image from Gyazo](https://i.gyazo.com/6333e8e8326ae9eb9c9fe48bb2cbb6e0.gif)](https://gyazo.com/6333e8e8326ae9eb9c9fe48bb2cbb6e0)

### 修正
1. 既存データのカラムをダブルクリックする
1. データを修正する
1. エンターボタンを押すと、修正データが保存される
    [![Image from Gyazo](https://i.gyazo.com/f31d7f6646267ed70cd3ff807d254117.gif)](https://gyazo.com/f31d7f6646267ed70cd3ff807d254117)

### 削除
1. 既存データのカラムを選択する
1. 右クリックでメニューを表示し、「行の削除」をクリックする
1. 「選択した行を削除」をクリックすると、データが削除される
    [![Image from Gyazo](https://i.gyazo.com/cea4dba5b67a1b59e3199d46e81a9bf9.gif)](https://gyazo.com/cea4dba5b67a1b59e3199d46e81a9bf9)

## 手順(コンソール)
### 起動
1. コンソールを起動する
    ```
    # アプリのディレクトリに移動
    % cd ~/[アプリ名]
    
    # コンソールを起動
    % rails c
    ```

### 新規作成
#### newメソッド+saveメソッド
1. [newメソッド](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#newメソッド)で新規データを作成する
1. [saveメソッド](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#saveメソッド)でデータを保存する

#### createメソッド
1. [createメソッド](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#createメソッド)で新規データを作成・保存する

### 修正
1. 修正対象のレコードを取得する
1. データを修正する
    ```ruby
    モデルのインスタンス名.カラム = 修正データ
    
    irb(main):002> post.content = "今日はお花見"
    => "今日はお花見"
    ```

### 削除


## 手順(アプリケーション)
アプリケーションの機能で、データの新規作成などを行う

# Ruby on Railsまとめ
https://qiita.com/likapantrip/items/46b43c9d1309b6633f0a

# テーブル
https://qiita.com/likapantrip/items/6e94b32d8ea9ad266964

# マイグレーションファイル
https://qiita.com/likapantrip/items/e49cbe55b998e4a7ff37
Reactにおけるリストレンダリング：mapとforループの比較

Reactでリストをレンダリングするとき、ほとんどの場面でmapメソッドを使いますよね。公式ドキュメントでもそうだし、ネット上の記事やチュートリアルもほぼすべてがmapで実装しています。
JavaScriptの基本的なループ構文であるforループは使うべきではないのか？とまとまっている記事がなかったので、まとめてみたのと、実際に検証してみることにしました。

## 基本的な実装方法
まずは両方の実装方法を見てみましょう。

### mapメソッドを使う方法
```tsx
function ListComponent({ items }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}
```
これが最もよく見かける方法です。JSX内に直接mapを書いて、配列の各要素に対応するJSX要素を返します。とてもシンプルですね。

### メリット
- 余計なコードがなく、直感的に書ける
- Reactの宣言的プログラミングスタイルと相性がいい
- 元の配列を変更せず、新しい配列を返す
- filterやreduceなどの他のメソッドとチェーンできる
- わざわざ変数に代入しなくてもいい


参考

https://qiita.com/espritfort_tech/items/c10c0d54feff69dffb05


### forループを使う方法
```tsx
function ListComponent({ items }) {
  const listItems = [];
  
  for (let i = 0; i < items.length; i++) {
    listItems.push(<li key={i}>{items[i]}</li>);
  }
  
  return <ul>{listItems}</ul>;
}
```
こちらはJSX外でforループを使って要素を配列に詰めていき、最終的にその配列をJSX内で展開します。

### メリット
- 多くのプログラマーが最初に学ぶループ構文
- 複雑な条件分岐やネストを柔軟に記述できる
- 関数呼び出しがないぶん、わずかに高速の可能性

## パフォーマンス検証
ここで気になるのがパフォーマンスです。理論上はforループのほうがオーバーヘッドが少ないはずですが、実際の違いはどれくらいあるのでしょうか？
検証するために、[シンプルなベンチマークツール](https://jyagamino.github.io/map-vs-for-performance/)を作りました。様々なサイズの配列に対して、両方のアプローチでJSX要素を生成する時間を計測しています。

https://github.com/jyagamino/map-vs-for-performance


### 検証結果
参考程度に私のマシン、Chrome135環境での検証結果です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285103/77464f10-4b5a-4d71-a259-9202d2f649ef.png)

ユーザーが体感できるレベルでの差はなさそうです。

## 実際のアプリ開発では？
パフォーマンス面だけで見ればforループに軍配が上がりますが、実際のアプリ開発では他の要素も考慮する必要があります。

特別な理由がない限り公式ドキュメントでも案内されているmapを使うことをお勧めします。

for文と比較して、そのメリットを整理すると、
- コードが簡潔で読みやすい
- Reactの宣言的スタイルと一致している
- Reactのほとんどの開発者が慣れている書き方
- パフォーマンスの差は実用上無視できるレベル

## まとめ
Reactでリストをレンダリングする際、mapメソッドは単なる習慣ではなく、宣言的な記述と相性が良いという合理的な理由で採用されています。わずかなパフォーマンスの差よりも、コードの簡潔さや保守性を優先すべきですね。
【DjangoのLogout機能が教本通り動かなかった経験から

# はじめに
わたしは現在djangoの学習中の実務未経験のエンジニア志望のサラリーマン（営業）です。
参考書は「実践Django Pythonによる本格Webアプリケーション開発」を進めています。
https://www.shoeisha.co.jp/book/detail/9784798153964
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4043863/bc3a0b03-47bf-4ea6-948f-e231e1868b8f.png)

# 躓いたこと
教本通りログイン・ログアウト機能を実施するコードを記載して、
ログイン中状態からログアウトしても　HTTP ERROR 405　が表示されてしまう。。
LogoutView.as_view()クラスを用いて、
setteing.pyでLOGOUT_REDIRECT_URL = "/"を設定しているので
教本通りならログアウトボタンを押せばトップページにリダイレクトされるはず。
~~~accounts/urls.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path
from django.views.generic import CreateView

urlpatterns = [
    path('signup/', CreateView.as_view(
        template_name='accounts/signup.html',
        form_class=UserCreationForm,
        success_url="/",
    ), name='signup'),
    path('login/', LoginView.as_view(
        redirect_authenticated_user=True,
        template_name='accounts/login.html'
    ), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]
~~~

~~~base.html
<nav class="navbar navbar-expand flex-md-row navbar-dark bg-dark">
    <div class="container justify-content-between">
        <a href="/" class="navbar-brand">Djangoスニペット</a>
        <ul class="navbar-nav mr-md-2">
            {% if user.is_authenticated %}
                <li class="nav-item"><a class="nav-link" href="{% url 'logout' %}">ログアウト</a></li>
            {% else %}
                <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">ログイン</a></li>
                <li class="nav-item"><a class="nav-link" href="{% url 'signup' %}">会員登録</a></li>
            {% endif %}
        </ul>
    </div>
</nav>
~~~

# 気付いたきっかけ
コマンドプロンプトに表示されていたエラーがこちら
~~~
Method Not Allowed (GET): /accounts/logout/
Method Not Allowed: /accounts/logout/
~~~
上記エラーで調べてみたところ、詳細を説明されているページを発見
https://terakoya.sejuku.net/question/detail/33882
ログアウトの際はPOSTメソッドでなくてはいけないところ、GETメソッドがリクエストされているとか・・・

# 解決策
説明をもとに＜a＞タグで囲っているところを＜form＞タグに修正
~~~base.html
<nav class="navbar navbar-expand flex-md-row navbar-dark bg-dark">
    <div class="container justify-content-between">
        <a href="/" class="navbar-brand">Djangoスニペット</a>
        <ul class="navbar-nav mr-md-2">
            {% if user.is_authenticated %}
                <li class="nav-item">
                    <form method="post" action="{% url 'logout' %}">
                        {% csrf_token %}
                        <button type="submit" class="btn btn-link nav-link">ログアウト</button>
                    </form>
                </li>
            {% else %}
                <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">ログイン</a></li>
                <li class="nav-item"><a class="nav-link" href="{% url 'signup' %}">会員登録</a></li>
            {% endif %}
        </ul>
    </div>
</nav>
~~~

# 最後に
無事ログアウトボタンが動作するようになりました。
（でもナビゲーションバーのログアウトがログイン・会員登録にパッと切り替わるだけで味気ないので、ログアウトしたら別のページに飛ぶ方が面白いなーと思いました。）
storybookを使ってみる

# storybookのinstallと立ち上げ
```
npx storybook@latest init
```
```
yarn storybook
```

# tailwind cssを使用する場合
./storybook/preview.tsに以下項目を追加
```diff_typescript
import type { Preview } from "@storybook/react";
+ import "@/app/globals.css";

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
};

export default preview;
```

# ./componetsにstorybookファイルを配置する場合<Br>(今回はcomponets/Buttonに配置)
./storybook/main.tsのstoriesに以下項目を追加
```diff_typescript
import type { StorybookConfig } from "@storybook/nextjs";

const config: StorybookConfig = {
  stories: [
    "../stories/**/*.mdx",
    "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
+   "../components/***/**/*.stories.@(js|jsx|ts|tsx)",
  ],
  addons: [
    "@storybook/addon-onboarding",
    "@storybook/addon-essentials",
    "@chromatic-com/storybook",
    "@storybook/addon-interactions",
  ],
  framework: {
    name: "@storybook/nextjs",
    options: {},
  },
  staticDirs: ["..\\public"],
};
export default config;

```
※他のディレクトリを参照したい場合は、その相対パスを指定する。

追記 25/04/13
以下のように記述することで、.stories.*の拡張子ファイルを読み込んでくれるようになる。
```
stories: ["../**/*.mdx", "../**/*.stories.@(js|jsx|mjs|ts|tsx)"],
```
https://zenn.dev/masatotezuka/articles/strorybook_nextjs_beginner

# 実際にstorybookで作成したコンポーネントを表示してみる
```typescript
interface IProps {
  bgColor?: string;
  onClick: () => void;
}

const Button = ({ bgColor, onClick }: IProps) => {
  return (
    <>
      <button
        onClick={onClick}
        className="border-2 rounded-md text-2xl p-2"
        style={{ backgroundColor: bgColor }}
      >
        test
      </button>
    </>
  );
};
export default Button;
```
```typescript
import type { Meta, StoryObj } from "@storybook/react";
import { fn } from "@storybook/test";

import Button from "./button";

const meta = {
  title: "componets/Button",
  component: Button,
  parameters: {
    layout: "centered",
  },
  tags: ["autodocs"],
  // storybookのconotrolsから色変更ができる(1)
  argTypes: {
    bgColor: { control: "color" },
  },
  // args: { onClick: fn() },
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Defaullt: Story = {
  args: { onClick: fn() },
};

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3779623/c36fa871-b71b-4922-a4b3-0f558b99a848.png) 

<br/> 
bgColorにコントローラーを設定しているため自由に色変更できるようになっている。(1)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3779623/fda1acb0-97e5-4186-a99b-8c3478b7cac6.png) 

storybookにはこれ以外にも様々な機能があるので、いろいろ試してみたい。
アクセスログに記録される時刻について

# 問題

ウェブアプリケーションで処理に時間がかかることがあり、その調査を行っている。
その時、アクセスログをもとに原因を特定しているのだが、利用しているミドルウェアによってアクセスログに記録されている時刻が異なっていて混乱したので、その内容を示す。

## 利用しているアーキテクチャ

今回のアーキテクチャは `CloudFront` -> `nginx` -> `Apache` の構成になっていて、それぞれでアクセスログを出力している。

## アクセスログ時刻の仕様

違いをまとめると以下の通り。 Apache は「受信時刻」だが、CloudFront/Nginx は「レスポンス完了時刻」を示すことが大きく違う。

| 項目                       | CloudFront                                                                                          | Nginx                                                                                       | Apache                                                                                      |
|----------------------------|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| 時刻フィールド | `date` + `time`                                                                                     | `$time_local`                                                                               | `%t`                                                                                        |
| ログの時刻の意味           | **レスポンス完了時刻**                                     | **レスポンス完了時刻**                                 | **リクエスト受信時刻**                           |
| ログ出力のタイミング       | レスポンス完了後                                                                                   | レスポンス完了後                                                                           | レスポンス完了後                                                                           |
| 公式ドキュメント           | [CloudFront 標準ログ仕様](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/standard-logs-reference.html) | [Nginx $time_local](https://nginx.org/en/docs/http/ngx_http_log_module.html) | [Apache ログファイル](https://httpd.apache.org/docs/2.4/ja/logs.html) |

そのため、例えば

- クライアントからのリクエスト開始は `00:00:00`
- Apache での処理に3秒程度かかる
- `クライアント - CLoudFront - Nginx - Apache` 間の通信のオーバーヘッドは考えないものとする

といった条件でログを記録した場合、ログの時刻記録は以下のようになる。

- CloudFront: `00:00:03`  (※time-taken などのリクエスト全体にかかった時間を知るための記録もあり、こちらには3秒が記録される）
- Nginx : `00:00:03`
- Apache: `00:00:00`

この仕様を知らないと、ログ時刻を誤って読み取って異なる結論を導き出してしまうので注意すること。
Laravelでルートの一覧の確認方法

以下の記事は、個人ブログ(https://yamachan228.seesaa.net/ )に掲載しているものの転載です。

開発中にいくらリクエストしても、APIが接続できず、NOT FOUNDになることが発生したことがありました。

本日は、その時の解決方法を共有します。

結論、以下のコマンドで、文法上のエラーを発見しました。

php artisan route:list

エラー内容は、変数のに$がついていないことが原因でしたが、なかなか糸口がつかめなかったため、助かりました。

この方法で、APIの通信エラーのすべてが原因箇所を特定できるわけではないかと思いますが、ヒントになることは、

あるかもしれません。

今日は、ここまで。
黄昏酒場スコアボード作成⑤〜その他のバックエンドとフロントエンド

## はじめに

④の続きです。

## データベース関連

ウェブサイト、データベースと来て一番最初に考えなければならないのはSQLインジェクションという攻撃である。
この攻撃はデータベースを操作するときのコマンド上でユーザが悪意のあるデータを入れると意図しないデータを持ってこれるという攻撃である。

```sql
--ユーザの入力したreplay_idに合致するreplayのメタデータを取得するSQL
SELECT * from replays where replay_id = {ユーザの入力} 

--ユーザの入力を [1 OR 1=1] とすることで全てのカラムで条件を満たし
--全てのreplayのメタデータを取得するSQL
SELECT * from replays where replay_id = 1 OR 1=1 
```
これは国の独立行政法人IPA曰くインシデント届出のうち11%に上り、非常にメジャーな脆弱性である。資料ページの一番最初に出てくる単語という時点でお察しである。
それ用のライブラリを使って対策を行った。

## パスワード関連

旧ロイヤルフレアでは1つのリプレイに対して1つの削除用パスワードが紐付く。
よって今回の実装でも削除パスワードを何らかの形で保管することにした。
しかしパスワードをそのまま保管しておくと万が一不正アクセスでパスワードを見られたときの被害が大きくなる。何よりサーバ管理する未来の自分がパスワードを使って良からぬことを企まないか心配である。
そこでハッシュという複合不可なデータ変換手法を使う。
リプレイ投稿時にパスワードをハッシュ化しておき、削除依頼が来たときは送られてきたパスワードをハッシュ化して手元のハッシュ化済パスワードと等しいか比べる。
ハッシュの安全性はハッシュのアルゴリズムによって異なる。コンピュータの発達によって昔は安全だとされていたハッシュアルゴリズムも危険になってしまう事がある。
また、安全とされるハッシュもレインボーテーブルという攻撃によって突破される可能性がある。これは事前に平文とハッシュ対応表のようなもの[^1]を作っておくことでハッシュが複合出来てしまうというものである。
アルゴリズムが固定ならそれ用のレインボーテーブルを作っておけば突破されてしまうのでハッシュに自分なりに一工夫加える必要がある。
一般にユーザの入力したパスワードに対してソルトやペッパーと言われるノイズを含ませたり、何回もハッシュ化させるストレッチングという手法を使うことで破られにくくする。

## フロントエンド

### vueとvuetify

一般にユーザが使用しているブラウザはJavaScriptに対応している。
このJavaScriptを使うことで動く画面を作ったり、ユーザがコマンドラインを叩かなくても画面を通してバックエンドのAPIを叩くことができる。
フロントエンドサーバはユーザ側ブラウザで実行してほしいJavaScriptを定義し、ユーザがアクセスしてきたときにそれを提供する役目を持つ。
フロントエンドを実装するにあたりJavaScriptでプログラミングしても良いのだがJavaScriptをラッピングしたvueというものがあり、使うだけでそれっぽい画面が作れるvuetifyというライブラリを使うことでそれらしい画面を作った。
vueもvuetifyも日本語ドキュメントがまとまっており、調べながら実装するのがバックエンド実装より容易だった。

### CORS

しかしフロントエンドからバックエンドに向かって通信しようとしたところうまくいかなかった。
これはどうやら `CORS` と `SOP` というものが関わっているらしい。
同一サーバの同一ポートで動くサービスをオリジンが等しいという。
同一オリジンでないとき、 `SOP` ( `Same Origin Policy` ) というものが働き、ブラウザが自主的に通信をブロックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659726/36bacdde-3f40-48c1-a1c2-2fff1faae7b4.png)
これがどういうときに効果を発揮するかというと
以下のように意図せず悪意のあるJavaScriptをブラウザが入手してしまった場合である。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659726/ae97e953-0b41-4f82-b77a-e301944cb1b5.png)
これを回避するためにブラウザはあらかじめJavaScriptによって通信される先のオリジンにはJavaScriptの発行元オリジンのことを知っているか聞いてから通信することになっている。
よってバックエンドで動いているfastapiにフロントエンドのオリジンを登録してあげなければならない。こうすることで初めて通信ができる。これを `CORS` ( `Cross-Origin Resource Sharing` )という。
ただしこれはあくまでブラウザが `SOP` に対応していないといけない。脆弱なブラウザを使うと普通に通信出来てしまうので注意である。

## 次回

アプリ開発が終わったのでネットワークの配置について勉強していく

## 参考文献

[安全なウェブサイトの作り方](https://www.ipa.go.jp/security/vuln/websecurity/about.html)

[^1]:正確には違うだろうけど許してくれ。
DockerでPlaywrightを動かしてみた

## 前段
クライアント管理ソフトウェアによる影響でローカルでPlaywrightが実行できなかったのでDockerで実行できる環境を構築してみた。

## Playwrightとは
Webブラウザのテスト自動化を実行するためのツール

* 対応ブラウザ：Chromium、WebKit、Firefox、Edgeなど
* プラットフォーム：Windows、Linux、macOS
* 言語：TypeScript、JavaScript、Python、.Net、Java

### 自動でやってくれること
* ページを開く
* ボタンをクリックする
* フォームに文字を入力する
* 表示されてるか確認する（テスト）
* スクリーンショットや動画を撮る
* 複数のブラウザで自動テスト（Chrome / Firefox / Safari）

## Dockerで動かす
* Docker上にPlaywright（とNode.js）をインストールし、そこでブラウザ付きのテストや操作を自動実行できるようにする
* テストの実行結果をホストPCに出力する

### テストコードを用意する
プロジェクトフォルダを作って、tests/example.spec.ts などにPlaywrightのテストを書く

```
import { test, expect } from '@playwright/test';

test('sample test', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example Domain/);
});
```

### 実行結果を出力するディレクトリを用意
```
mkdir -p results
```

### Playwright.config.tsで実行結果の出力先を設定
コンテナ内の playwright-report フォルダにHTMLレポートなどが出力されるようになる
```
reporter: [['html', { outputFolder: 'playwright-report', open: 'never' }]],
```

### Dockerfileを作成する
```
# Dockerfile
FROM mcr.microsoft.com/playwright:v1.43.1-jammy

# 作業ディレクトリを作成
WORKDIR /app

# package.jsonとpackage-lock.jsonをコピー
COPY package*.json ./

# 依存パッケージをインストール
RUN npm install

# これを忘れずに！
RUN npx playwright install

# アプリのソースコードをコピー
COPY . .

# テスト実行（任意）
CMD ["npx", "playwright", "test"]
```

### Dockerをビルドする
```
docker build -t your-playwright-image .
```

### Dockerを実行する
Docker実行時に実行結果をホスト側にマウントする
* -v オプションでホストの ./results をコンテナの /app/playwright-report に接続
* コンテナ内のレポート出力がそのままホストPCに反映される
```
docker run --rm \
  -v $(pwd)/results:/app/playwright-report \
  your-playwright-image
```

## まとめ
ローカルに比べて手間ではあるが、DockerでもPlaywrightを動かすことができた。
codegenを使ったテストコードの自動生成ができないため、こちらもDockerを使って実施できるか検証してみる。
Azure Bastionが日本でも無料で利用できるようになりました

## 概要
2025年3月末にAzure Bastion Developerが日本リージョンでも使用可能になりました。
[Azure Bastion の価格](https://azure.microsoft.com/ja-jp/pricing/details/azure-bastion/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298070/8bd7c373-cae5-4136-a514-723967b0f5cf.png)
[Quickstart: Connect with Azure Bastion Developer](https://learn.microsoft.com/ja-jp/azure/bastion/quickstart-developer)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298070/24ed037c-9261-44b1-b5b9-a6a58288adfa.png)

## 使用方法
Azure Bastion画面から新規作成でSKU(レベル)でDeveloperを選択して作成するか、VMの接続画面から「Bastionのデプロイ」を選択した場合も自動でDeveloperで作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298070/4228a1a2-edd9-4397-b8de-89914128fa93.png)

## 注意点
以下表の通り、DeveloerSKUでは一部機能が制限されています。
全機能を使用する場合はSKUのグレードアップ検討が必要です。

[Bastion の構成設定について](https://learn.microsoft.com/ja-jp/azure/bastion/configuration-settings)
|機能|Developer SKU|Basic SKU|Standard SKU|Premium SKU|
|:----|:----|:----|:----|:----|
|同じ仮想ネットワーク内のターゲット VM に接続する|〇|〇|〇|〇|
|ピアリングされた仮想ネットワーク内のターゲット VM に接続する|×|〇|〇|〇|
|コンカレント接続のサポート|×|〇|〇|〇|
|Azure Key Vault (AKV) で Linux VM のプライベート キーにアクセスする|〇|〇|〇|〇|
|SSH を使用した Linux VM への接続|〇|〇|〇|〇|
|RDP を使用した Windows VM への接続|〇|〇|〇|〇|
|RDP を使用した Linux VM への接続|×|×|〇|〇|
|SSH を使用した Windows VM への接続|×|×|〇|〇|
|カスタム受信ポートの指定|×|×|〇|〇|
|Azure CLI を使用して VM に接続する|×|×|〇|〇|
|ホストのスケーリング|×|×|〇|〇|
|ファイルのアップロードまたはダウンロード|×|×|〇|〇|
|Kerberos 認証|×|〇|〇|〇|
|共有可能リンク|×|×|〇|〇|
|IP アドレスを使用して VM に接続する|×|×|〇|〇|
|VM オーディオ出力|〇|〇|〇|〇|
|コピー/貼り付けを無効にする (Web ベースのクライアント)|×|×|〇|〇|
|セッションの記録|×|×|×|〇|
|プライベート専用のデプロイ|×|×|×|〇|
pdf-lib：カスタムフォントを埋め込む

皆さん、こんにちは。

今回は「pdf-lib：カスタムフォントを埋め込む」について紹介させていただきます。

`pdf-lib`では、標準フォント（`Helvetica`、`Times Roman`、`Courier`など）以外の独自フォント（カスタムフォント）を使用するために、フォントファイル（.ttf や .otf など）を読み込み、埋め込む必要があります。以下では、ブラウザ環境での使用例と Node.js 環境での使用例を含め、具体的な手順を詳しく解説します。

# カスタムフォントを埋め込む基本手順

1. フォントファイルの用意:
使用したいフォントファイル（例: MyCustomFont.ttf）をプロジェクト内に用意します。もし日本語フォントを利用する場合、文字セットをサポートしているフォント（例: Noto Sans CJK や IPAexフォントなど）を選びます。

2. フォントファイルの読み込み:
ブラウザの場合は、fetch を使って ArrayBuffer として読み込みます。Node.js では、fs モジュールでファイルを読み込むことができます。

3. PDFDocument へのフォントの埋め込み:
PDFDocument のインスタンスに対して、embedFont メソッドを使って読み込んだフォントデータを埋め込みます。これにより、描画時にカスタムフォントが利用できるようになります。

4. テキスト描画時にフォントを指定:
page.drawText のオプションに font: customFont を渡すことで、カスタムフォントでテキストを描画できます。

## ブラウザ環境での実装例

```javascript
import { PDFDocument, rgb } from 'pdf-lib';

async function createPdfWithCustomFont() {
  // PDFドキュメントの作成
  const pdfDoc = await PDFDocument.create();

  // ページの追加
  const page = pdfDoc.addPage();
  const { width, height } = page.getSize();

  // フォントファイルの読み込み（例: public/fonts/MyCustomFont.ttf）
  const fontUrl = '/fonts/MyCustomFont.ttf';
  const fontBytes = await fetch(fontUrl).then((res) => res.arrayBuffer());

  // カスタムフォントの埋め込み
  const customFont = await pdfDoc.embedFont(fontBytes);

  // テキスト描画
  const fontSize = 24;
  page.drawText('こんにちは、世界！', {
    x: 50,
    y: height - 4 * fontSize,
    size: fontSize,
    font: customFont,
    color: rgb(0, 0.53, 0.71)
  });

  // PDFバイトデータの取得（Blob に変換してダウンロード等が可能）
  const pdfBytes = await pdfDoc.save();

  // ブラウザで PDF を表示またはダウンロードするための処理例
  const blob = new Blob([pdfBytes], { type: 'application/pdf' });
  const url = URL.createObjectURL(blob);
  window.open(url);
}

createPdfWithCustomFont();

```

### 補足ポイント

- ファイルパス:
ブラウザ環境の場合、フォントファイルは正しい URL パスに配置してください。たとえば、public フォルダに配置し、`/fonts/MyCustomFont.ttf` として参照する形になります。

- エラーハンドリング:
実際のアプリケーションでは、fetch の失敗やファイルが存在しない場合に備えてエラーハンドリングを追加することをお勧めします。

## Node.js 環境での実装例

Node.js 環境で PDF を作成する場合は、fs モジュールでフォントファイルを読み込み、同様の手順でカスタムフォントを埋め込みます。

```javascript
const fs = require('fs');
const { PDFDocument, rgb } = require('pdf-lib');

async function createPdfWithCustomFont() {
  // PDFドキュメントの作成
  const pdfDoc = await PDFDocument.create();

  // ページの追加
  const page = pdfDoc.addPage([600, 400]);

  // フォントファイルの読み込み
  const fontBytes = fs.readFileSync('path/to/MyCustomFont.ttf');

  // カスタムフォントの埋め込み
  const customFont = await pdfDoc.embedFont(fontBytes);

  // テキスト描画
  const fontSize = 24;
  page.drawText('こんにちは、世界！', {
    x: 50,
    y: 350,
    size: fontSize,
    font: customFont,
    color: rgb(0, 0, 0)
  });

  // PDFバイトデータの取得とファイルへの書き出し
  const pdfBytes = await pdfDoc.save();
  fs.writeFileSync('output.pdf', pdfBytes);
}

createPdfWithCustomFont();

```

pdf-lib の場合、組み込みの標準フォントは日本語をサポートしていません。従って、日本語を扱う場合は、必ず日本語の文字が含まれるカスタムフォントを埋め込む必要があります。例えば、Google の Noto Sans CJK や IPAexフォントを使用するとよいでしょう。同様の手順でフォントファイルを読み込み、埋め込み、テキスト描画時に指定します。

# まとめ
pdf-lib で別のフォントを指定する際は、以下の流れになります：

1. カスタムフォントファイルを用意し、ArrayBuffer（もしくは Buffer）の形式で読み込む
2. pdfDoc.embedFont メソッドを使って PDF にフォントを埋め込む
3. 描画時に font プロパティで埋め込んだフォントを指定する

これにより、標準フォント以外の任意のフォントを用いて PDF を作成でき、デザインの自由度が大いに向上します。

今日は以上です。

ありがとうございました。
よろしくお願いいたします。
FlutterでWidgetにconstをつける理由

# この記事を書いた経緯

今回Flutterの学習をしている中でWidgetやコンストラクタの前になどにconstをつけることが多くなぜつけるのか気になり調べたものをまとめてみました。

----
FlutterでWidgetを定義する際、以下のように`const`を付けることが推奨されるケースがあります。

```dart
const Text('Hello, Flutter!');
```

## なぜconstをつけるのか？

FlutterにおいてWidgetに`const`をつける主な理由は、パフォーマンスの最適化にあります。

`const`をつけることで、FlutterはそのWidgetが再描画や再ビルドされる必要がないことを明示的に理解します。
これにより、次のメリットが得られます。

### 1. ビルドパフォーマンスの向上

同じWidgetを何度も作成する必要がなく、メモリ上の同一インスタンスを再利用します。これにより、無駄なオブジェクト生成が減り、パフォーマンスが向上します。

### 2. 再描画の効率化

Widgetツリーに変更が生じた際、Flutterは`const` Widgetに関しては変更がないと判断し、その部分の描画をスキップできます。再描画範囲が最小限になり、レンダリング効率が高まります。

## constを使うべきWidgetの特徴

以下の条件に該当するWidgetは、積極的に`const`を使いましょう。

- 変更される可能性がない（静的な）Widget
- 常に同じ内容を表示する（テキスト、アイコン、静的な装飾など）
- 親Widgetもconstとして定義されている

## constを使った例

```dart
class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.star, size: 50),
          SizedBox(height: 10),
          Text('Hello, World!'),
        ],
      ),
    );
  }
}
```

## constがつけられない場合

以下の条件に該当するWidgetは、`const`をつけることができません。

- 実行時に動的に値が変わる（ユーザー入力やAPIのレスポンスを表示する場合など）
- 状態管理によって変化する値を表示する場合

こうしたWidgetでは無理に`const`をつけず、そのまま通常のインスタンスとして扱いましょう。

```dart
class DynamicWidget extends StatelessWidget {
  final String dynamicText;

  const DynamicWidget({super.key, required this.dynamicText});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(dynamicText), // ここは動的な値なのでconstがつけられない
    );
  }
}
```

## まとめ

Flutterにおいてconstは、パフォーマンスを効率化し、不要な再描画を防ぐために積極的に活用すべきキーワードです。状況に応じてconstを適切に付けて、アプリのパフォーマンスを最適化しましょう。
MCPについて理解する１

はてなブログを見ればMCPMCPと盛り上がっているよう？なので管轄外ではありますが、興味を持った今日この頃。
久しぶりに時間もあるので時代に取り残されないように調べてみました。

# MCPってなんぞ？
ModelContextProtocol直訳したらモデルの文脈に関する手順って意味になりますが、全くその通りっぽくて

AIアシスタントが、外部データやツールにアクセスするための共通ルールのことを指している

MCPを使用することでAI <-> データ・ツールの接続方式を標準化することができるっぽい

# 共通ルールについて知る
MCPについて軽く理解をする上で以下のルールは知っておかないといけない

1. 共通言語はJSON-RPC 
    例えば多国籍な人らと会議することになったら必要となるルールが共通言語。英語で話そう！というようにMCPではJSON-RPCでやり取りしよう！ってなっている

2. initializeの役割
    クライアントがサーバーに呼びかけを行う際には必ず**Initialize**というメソッドを送る
    状況としては「自分の持っている機能を持っているよ！！」と「どんな機能を持っている？？」というような自己紹介をしているみたいです

3. 1.2.のフローが終わった後
    初期化が終わると、登録された関数の呼び出しだったり、何でもかんでも楽しめるらしい

# 理解すべき概念
MCPは**Host**/**Client**/**Server**/の三つの役割で成り立っています。

まず、ホストが複数のクライアントを作成します
次に作成されたクライアントがサーバーと1on1で通信を行い、AI機能を拡張させているという構造になっています。

![スクリーンショット 2025-04-11 22.56.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1162351/9db638bd-b8c2-4909-bd2e-78c6edfd56f9.png)

適当に参考にしてきて書いた(こんな感じらしい)


# Host
ホストは軸の部分で以下の役割を持っている
・クライアントの作成と管理
・AIとの連携と指示
・複数のサーバーから仕入れた情報をもとに最適化を行う

# Client
クライアントは特定サーバーとセッションすることを役割としている
・１クライアントにつきサーバーは一つという制限
    それぞれ、DBサーバー用のクライアント・gitやファイルについてのクライアントなど一つのクライアントには一つしか役割を与えることができない。
・JSON-RPCで通信を行う
・通知やイベントに対応する
サーバーから受け取ったログや通知はクライアント側で受付、必要に応じてホストにフィードバックを行う。

# Server
サーバーはAIが活用できる機能やデータを公開する役割
* ツールなどの提供
    例えば、データベースの照会・ファイルの更新など色々な機能をエンドポイントとして公開してそれぞれはクライアントによって呼び出される
* ホストや他のサーバーにアクセスすることはできない
    セキュリティを高い水準で保持するためにもあくまでクライアントの属人化のような役割でいる
    そのため、他サーバーや内部データに触れることはできない


土日は結局暇になるんで実際に作ってみます(ハナホジ)
NextAuth.js (Auth.js) のBroadcast機構

[SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider) (v5は[こちら](https://authjs.dev/reference/nextjs/react#sessionprovider)) には、複数のタブでアプリケーションを表示している時に、タブ間でセッション状態を同期する仕組みがある。1つのタブでクライアントサイドからAPI呼び出しが発生したことをトリガに、他のタブにメッセージを送って (この機構はBroadcastと呼ばれる) 、クライアントサイドでのセッション取得 (`GET /api/auth/session`) を引き起こすことで実現されている。

#### ドキュメント上の記載

この機構についてはNextAuth.jsのドキュメントに細かく記載されていないが、該当するのは[Client API](https://next-auth.js.org/getting-started/client)の下記の箇所となる。

> Using the supplied `<SessionProvider>` allows instances of `useSession()` to share the session object across components, by using [React Context](https://react.dev/learn/passing-data-deeply-with-context) under the hood. It also takes care of keeping the session updated and synced between tabs/windows.

> Every tab/window maintains its own copy of the local session state; the session is not stored in shared storage like localStorage or sessionStorage. Any update in one tab/window triggers a message to other tabs/windows to update their own session state.

#### ソースコード上の実装箇所

Broadcastの実装は[src/client/_utils.ts](https://github.com/nextauthjs/next-auth/blob/v4/packages/next-auth/src/client/_utils.ts#L89)にある。仕組みはシンプルで、メッセージ送信時にLocalStorageにデータを書き込み、各タブは[storageイベント](https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event)をListenしておくことでメッセージを受信するという方式となっている。

#### Broadcastが発生するタイミング

NextAuth.jsのv4とv5で、どの操作のタイミングでBroadcastが発生するかを調査した。

| 操作                       | v4                                  | v5                                 |
| -------------------------- | ----------------------------------- | ---------------------------------- |
| サインイン                 | 発生しない                          | 発生しない                         |
| ページ表示 session注入あり | 発生しない                          | 発生しない                         |
| ページ表示 session注入なし | 発生する (`GET /auth/api/session`)  | 発生する (`GET /auth/api/session`) |
| サインアウト               | 発生する (`POST /auth/api/signout`) | 発生しない                         |

※ 「session注入」はSessionProviderにSSRで取得したsessionオブジェクトを渡す実装のことを指す。

※ () 内はBroadcastのトリガとなる通信。

##### 補足

* v4では `npm run dev` ([Strict Mode](https://react.dev/reference/react/StrictMode)有効) で動かすと、session注入ありでもBroadcastが発生する。
* v5ではセッション取得が複数回発生する。詳細は未確認だが、[このIssue](https://github.com/nextauthjs/next-auth/issues/10244)が関係しているようである。

#### 参考

* [Various issues with refresh token rotation · Issue #2071 · nextauthjs/next-auth](https://github.com/nextauthjs/next-auth/issues/2071)
【完全ガイド】Databuttonでカスタムドメインを設定する方法（Route 53対応）

# 【完全ガイド】Databuttonでカスタムドメインを設定する方法（Route 53対応）

Databuttonで作成したアプリを独自ドメインで公開したい──。  
この記事では、AWS Route 53 を使って **Databuttonアプリにカスタムドメインを割り当てる方法**をステップごとに丁寧に解説します。  
さらに、Firebase Authentication を併用している場合に必要な「承認済みドメイン」設定についてもカバーします。

---

## ✅ 前提条件

- Databuttonでアプリを作成済み（公開URLあり）
- AWS Route 53でドメインを管理している
- 使用する独自ドメインが `app.example.com` のようなサブドメイン形式である

---

## 🔧 ステップ 1：Databuttonでカスタムドメインを申請

1. Databuttonのアプリ編集画面を開く  
2. 上部メニューから `Settings` → `Custom Domain` に移動  
3. 使用したいドメイン（例：`app.example.com`）を入力  
4. `Continue` をクリックすると、DNSに設定すべき **CNAMEレコード** が表示される

### 📋 表示される内容（例）：
Name: app.example.com
Type: CNAME
Value: abc1234567890.cname.databutton.io

---

## 🛰 ステップ 2：Route 53 にCNAMEレコードを追加

1. AWSマネジメントコンソール → Route 53 → 対象ドメインの「ホストゾーン」を開く  
2. 「レコードを作成」をクリック  
3. 以下のように設定：

| 項目       | 内容                                          |
|------------|-----------------------------------------------|
| レコード名 | `app`（= `app.example.com` の `app` 部分）   |
| タイプ     | `CNAME`                                       |
| 値         | `abc1234567890.cname.databutton.io`（例）     |
| TTL        | 300（デフォルトでOK）                         |

4. レコードを保存

---

## ⏳ ステップ 3：DNS反映を待つ

DNSの反映には通常数分〜30分ほどかかります。

### ✅ 動作確認：

ブラウザで `https://app.example.com` にアクセスし、Databuttonのアプリが表示されれば設定完了です 🎉

---

## 🔐 HTTPS対応について

Databuttonはカスタムドメインを登録すると、**自動でSSL証明書（HTTPS）を設定**してくれます。  
そのため、Let's Encrypt などの証明書発行作業は不要です。

---

## 🛡 Firebase Authenticationを使っている場合の注意

Databuttonで Firebase Authentication（例：Googleログイン）を使用している場合は、  
**Firebase 側にもカスタムドメインを「承認済みドメイン」として追加**する必要があります。

### 🔧 設定手順：

1. Firebase コンソールを開く  
2. 対象プロジェクトを選択 → 「Authentication」→「Sign-in method」へ  
3. ページ下部の「**Authorized domains（承認済みドメイン）**」までスクロール  
4. 「**Add domain**」をクリックし、`app.example.com` を追加  
5. 保存

これを行わないと、OAuth認証時に次のようなエラーが出ます：

> **This domain is not authorized for OAuth operations.**

---

## ⚠️ その他の注意点

- Apexドメイン（`example.com`）を使いたい場合は、CNAMEではなく ALIAS or Aレコードを使用する必要があります。
- すでに同じサブドメインに他のレコードが存在している場合、競合するので削除してください。

---

## ✅ まとめ

| 項目                   | 内容                                         |
|------------------------|----------------------------------------------|
| 対応サービス           | Databutton + AWS Route 53                    |
| 対応ドメイン形式       | `subdomain.example.com`（CNAME対応）        |
| HTTPS証明書            | 自動発行（Databuttonが対応）                |
| Firebase Auth 対応     | 承認済みドメイン登録が必要                  |
| 所要時間               | 約10〜15分                                   |

Databuttonは爆速でアプリを公開できるだけでなく、**カスタムドメイン設定も非常にスムーズ**です。  
ブランドURLで公開したい、独自ドメインでAIアプリを運用したいという人にはおすすめの構成です。

---

## 🔗 関連リンク

- [Databutton公式サイト](https://databutton.com?via=takahiro19)
- [AWS Route 53 ドキュメント](https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/Welcome.html)
- [Firebase Authentication ドキュメント](https://firebase.google.com/docs/auth)
- https://ronbun-ai.org
AES-GCM + PBKDF2 + ハッシュチェーンで構築する堅牢なファイル暗号化ツールの設計と実装

こんにちは。今回は、AES-GCM暗号化・PBKDF2による鍵導出・ハッシュチェーンによるログ管理を組み合わせた、安全かつ実用的なファイル暗号化ツールの設計について紹介します。
---

## 🔒 背景と目的

最近の情報漏洩や内部不正の事例からも分かる通り、**暗号化だけでは不十分**なケースが増えています。

そこで本記事では、以下のようなセキュリティ要件を満たすシステム構成を紹介します：

- ✅ ファイルの機密性を守る
- ✅ 改ざんの検出と防止が可能
- ✅ 操作履歴を安全に保管・検証できる

---

## ⚙️ 使用技術一覧

AES-GCM: 認証付き暗号。暗号化と同時に改ざん検出を実現。 
PBKDF2 + Salt:パスワードからセキュアな鍵を導出。Saltにより攻撃耐性強化。
ハッシュチェーン:ログ同士をハッシュで連結し、改ざん不可な操作履歴を実現。 

---

## 🧩 システム構成フロー

1. ユーザーがパスワードを入力
2. PBKDF2 + Salt で暗号鍵を導出
3. AES-GCM でファイルを暗号化 / 復号
4. 操作ログをハッシュチェーンとして記録
5. ログの整合性検証が可能

---

## 🗃️ ログのハッシュチェーン例

```text
[ログ1]
timestamp: 2025-04-11 10:00
action: encrypt file1.txt
hash: SHA256("ファイル内容 + 初期固定値")

[ログ2]
timestamp: 2025-04-11 10:05
action: decrypt file1.txt
hash: SHA256("ファイル内容 + ログ1のハッシュ")

[ログ3]
timestamp: 2025-04-11 10:10
action: re-encrypt file1.txt with new password
hash: SHA256("ファイル内容 + ログ2のハッシュ")

ログチェーンが破損していないかを検証することで、過去の操作ログの信頼性が確保されます。
```
⸻

🔐 セキュリティ設計のポイント
	•	AES-GCM により、暗号化と改ざん検出を1ステップで実現
	•	PBKDF2（推奨反復回数：100,000回以上） によりブルートフォース攻撃を困難に
	•	Saltの導入 によって同じパスワードでも異なる鍵を生成
	•	IV（初期化ベクトル）は毎回ランダム生成
	•	ログは一方向性ハッシュチェーンで管理され、改ざんを検知可能

⸻

🌱 今後の拡張アイデア
	•	鍵のローテーション（再暗号化）
	•	ログ専用ビューアの実装と改ざん検出インターフェースの提供

⸻

✅ まとめ

今回紹介した構成は、以下の3つの目的を同時に達成できます：
	•	ファイルの暗号化（機密性の確保）
	•	鍵の安全な生成（強力なパスワードベース鍵導出）
	•	改ざん検出可能なログ記録（透明性と可監査性）

個人利用から中小企業向けのセキュリティ対策としても有効なアーキテクチャです。

⸻

📚 参考リンク
	•	<a href="https://datatracker.ietf.org/doc/html/rfc5116">RFC 5116 - AES-GCM</a>
	•	<a href="https://datatracker.ietf.org/doc/html/rfc8018">RFC 8018 - PBKDF2</a>
	•	<a href="https://ja.m.wikipedia.org/wiki/ハッシュチェーン">ハッシュチェーン - Wikipedia</a>

⸻

✍️ 最後に

この記事が参考になった方は、いいね・ストック・コメントお待ちしています！
実装例やPythonコードの公開も準備中ですので、ご期待ください！
スマートコントラクトがデジタルの世界を再構築している理由 —— そしてConfidence Technologyがその最前線にいる理由

スマートコントラクトがデジタルの世界を再構築している理由 —— そしてConfidence Technologyがその最前線にいる理由
急速に進化するデジタルイノベーションの世界において、ブロックチェーンはゲームチェンジャーとしての地位を確立しました。そしてこの変革の中心にあるのが「スマートコントラクト」です。

スマートコントラクトは、仲介者を排除し、信頼をコードそのものに委ねることで、新たな分散型アプリケーション（dApps）の時代を切り開いています。

あなたがもし、DeFiプロトコル、NFTコレクション、DAOなどのブロックチェーンプロジェクトを構想しているなら、その第一歩は安全で効率的なスマートコントラクトを開発できる信頼できるパートナーを見つけることです。
そこで登場するのが、Confidence Technologyです。

💡 スマートコントラクトとは？
スマートコントラクトとは、特定の条件が満たされたときに自動的に実行されるブロックチェーン上のプログラムです。変更不能で透明性が高く、自律的に動作するため、デジタル契約や支払い、トークン管理などに最適です。

実際のユースケース例:
暗号通貨（ERC-20やBEP-20トークンなど）の発行・管理

NFTのミント、ロイヤリティシステム、市場ロジックの実装

DeFiにおけるステーキング、ファーミング、流動性プール

DAOおよびクラウドファンディングプラットフォーム

定期支払いやデジタルライセンスの自動化

🤝 開発パートナー選びが重要な理由
理論上はシンプルに見えるスマートコントラクトですが、安全に開発・運用することは非常に高い技術力を要します。
たった一つの脆弱性で、何百万ドルもの損害が発生する可能性があります。

そのため、多くのWeb3プロジェクトがスマートコントラクト開発のエキスパートである
Confidence Technologyをパートナーに選んでいます。

🧠 Confidence Technologyが選ばれる理由
✅ 1. 多様なスマートコントラクトの開発に対応
NFTからDeFi、DAO、トークンローンチに至るまで、Confidence Technologyはあらゆるユースケースに対応。パフォーマンスとガス効率を重視したスマートコントラクトを提供します。

主なサービス内容:

ERC-20、ERC-721、ERC-1155トークンの開発

NFTオークションおよびロイヤリティ機構

流動性ロック、ベスティング、ステーキング機能

プリセール＆クラウドセールコントラクト

DAO用ガバナンス機構の構築

✅ 2. セキュリティ重視のアプローチ
セキュリティは何よりも最優先です。Confidence Technologyは再入可能性、整数オーバーフロー、ロジックエラーなどのリスクを排除するため、手動＋自動のコード監査を徹底しています。

✅ 3. フロントエンドとのWeb3統合サポート
スマートコントラクト開発に加え、dAppやウォレット、ダッシュボードとの統合までワンストップでサポート。Ethers.js, Web3.js, Reactなどの最新技術を活用します。

✅ 4. マルチチェーン対応
Ethereum以外にも、多くのEVM互換チェーンに対応可能です。

Binance Smart Chain（BSC）

Polygon

Avalanche

Arbitrum

Optimism

Fantom

Base

Solana（Rustベース）

🌐 グローバルなブロックチェーン開発パートナー
Confidence Technologyは、個人開発者からWeb3スタートアップまで、世界中のクライアントと協業しています。MVP段階から本番環境へのデプロイ、さらにはリリース後のサポートまでを一貫して提供します。

NFTプロジェクト、プレセールの実施、DeFiプロトコルの構築など、あらゆるブロックチェーンビジョンを実現します。

🔮 結論：未来はスマートコントラクトの中にある
スマートコントラクトは一過性のトレンドではなく、Web3の中核を担う技術です。
この技術を早期に取り入れた企業・プロジェクトが、次世代の競争優位を得ることができます。

あなたのプロジェクトをブロックチェーンで形にする準備ができているなら、
まずはConfidence Technologyにご相談ください。

👉 スマートコントラクト開発の詳細はこちら: https://confidence-tech.com

未来は、コードで契約される時代へ。
https://confidence-tech.com/
https://confidence-crm.com/
https://diamond-firms.com/