Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

kvtool について

kvtool は、様々な設定ファイル形式(.env、JSON、YAML、Vault、S3 など)を統一的なインターフェースで扱う CLI ツールです。

特徴

  • 統一インターフェース: ローカルファイル、Vault、S3 などを同じ方法でアクセス
  • マルチテナント: namespace による環境切り替え
  • フォーマット変換: dotenv ↔ JSON ↔ YAML
  • 接続確認: ストアの接続テスト機能

ユースケース

環境構築の初期段階で、様々な構成ファイルを統合して環境を初期化することを想定しています。

コマンド体系

kvtool
├── file (低レベル: 直接アクセス)
│   ├── load (env, vault, json, yaml, etc.)
│   └── convert (dotenv, yaml)
└── store (高レベル: 設定ファイル経由)
    ├── init
    ├── connect
    ├── load
    └── serve (未実装)

ライセンス

MIT

インストール

ビルドとインストール

Makefileを使用

# ビルドとインストール
make install

直接ビルド

# バイナリをbin/ディレクトリに生成
go build -o bin/kvtool .

動作確認

kvtool --version
kvtool --help

必要な環境

  • Go 1.21 以上
  • (オプション)HashiCorp Vault(Vault 連携時)
  • (オプション)AWS CLI/認証情報(S3 連携時)

クイックスタート

設定ファイルの初期化

# カレントディレクトリに .kvtool.yml を作成
kvtool store init

# グローバル設定ファイル (~/.config/kvtool/.kvtool.yml) を作成
kvtool store init --global

ストアからデータ取得

# .envストアからAPP_NAMEを取得(JSON形式で出力)
kvtool store load .env/APP_NAME

# key=value形式で出力
kvtool store load .env/APP_NAME -o raw

# Vaultから取得
kvtool store load vault/db/password

# S3から取得
kvtool store load s3config/app.json

接続確認

# 設定ファイル内の全ストアをテスト
kvtool store connect

# 特定のストアのみテスト
kvtool store connect s3-test

ファイル直接操作(低レベル)

設定ファイルを使わず、直接ファイルシステムにアクセスする場合:

# 環境変数をJSON化
kvtool file load env

# Vaultから直接取得
kvtool file load vault <path> --addr http://localhost:8200 --token root

# 標準入力からdotenv変換
cat .env | kvtool file convert dotenv

設定例

.kvtool.yml の例:

version: 0.1
namespaces:
  default:
    local-env:
      driver: local
      args:
        root: ./config
        transform: dotenv
    vault-prod:
      driver: vault
      args:
        addr: http://localhost:8200
        token: root
        mount: secret
    s3-config:
      driver: s3
      args:
        bucket: my-bucket
        region: us-east-1
        endpoint: http://localhost:9000

ストア構成ファイル

kvtool のストア構成ファイルは YAML 形式 (.kvtool.yml) または HCL 形式 (.kvtool.hcl) で記述できます。

設定ファイルの配置

以下の順序で設定ファイルを探します:

  1. カレントディレクトリの .kvtool.yml または .kvtool.hcl
  2. --config フラグで指定されたパス
  3. グローバル設定: ~/.config/kvtool/.kvtool.yml

基本構造(YAML)

version: 0.1
namespaces:
  default:
    local:
      driver: local
      args:
        root: ./config
    vault:
      driver: vault
      args:
        addr: http://localhost:8200
        token: ${VAULT_TOKEN}
  production:
    vault-prod:
      driver: vault
      args:
        addr: https://vault.example.com
  • namespaces: 環境(開発、本番など)ごとに設定を切り替える仕組み
  • デフォルトは default namespace を使用
  • 各ドライバーの設定パラメータは ドライバー を参照

HCL 形式

HCL は変数定義や環境変数展開をサポートします。

locals {
  env = "development"
}

namespaces {
  namespace "default" {
    vault {
      driver = "vault"
      args {
        addr  = env.VAULT_ADDR
        token = env.VAULT_TOKEN
        path  = "secret/${local.env}/app"
      }
    }

    local {
      driver = "local"
      args {
        root = "./configs/${local.env}"
      }
    }
  }
}

HCL 固有機能

機能構文説明
変数定義locals { ... }辞書形式で変数を定義
変数参照local.config.port属性アクセスで変数を参照
環境変数env.VAR_NAME環境変数を直接参照
文字列補間"${local.var}"文字列内で変数を展開

YAML と HCL の対応

YAMLHCL
namespaces:namespaces {
default:namespace "default" {
driver: vaultdriver = "vault"
args:args {
${VAR}env.VAR

HTTP サーバー

kvtool store serve コマンドで、設定ファイルで定義したストアを REST API として公開できます。

起動方法

# デフォルト設定(ポート 8080)
kvtool store serve

# ポート指定
kvtool store serve --port 3000

# グローバル設定を使用
kvtool store serve --global

エンドポイント

KV エンドポイント(JSON)

GET /v1/namespaces/{namespace}/kv/{store}/{path}

ファイルを JSON としてパースして返します。

# production 環境の vault ストアから app/settings を取得
curl http://localhost:8080/v1/namespaces/production/kv/vault/app/settings

# default 環境の local ストアから config.json を取得
curl http://localhost:8080/v1/namespaces/default/kv/local/config.json

Storage エンドポイント(Raw)

GET /v1/namespaces/{namespace}/storage/{store}/{path}

ファイルをそのまま(application/octet-stream)返します。

# production 環境の s3 ストアから data.bin を取得
curl http://localhost:8080/v1/namespaces/production/storage/s3/data.bin

ヘルスチェック

GET /health
curl http://localhost:8080/health
# => ok

設定例

version: 1.0
namespaces:
  production:
    vault:
      driver: vault
      args:
        address: https://vault.example.com
        token_env: VAULT_TOKEN
        mount: secret
        root: myapp
    s3:
      driver: s3
      args:
        bucket: prod-configs
        region: ap-northeast-1

  development:
    local:
      driver: local
      args:
        root: ./configs/dev

この設定で以下のエンドポイントが利用可能になります:

  • /v1/namespaces/production/kv/vault/{path}
  • /v1/namespaces/production/kv/s3/{path}
  • /v1/namespaces/development/kv/local/{path}

エラーレスポンス

ステータス説明
404namespace、store、またはファイルが存在しない
405GET 以外のメソッド
500サーバー内部エラー

CLI オプション

オプション短縮デフォルト説明
--config-c.kvtool.yml設定ファイルパス
--global-gfalseグローバル設定を使用
--port-p8080リッスンポート

Kubernetes 連携

kvtool を Kubernetes のシークレットプロバイダーとして使用する方法を説明します。

連携方法の比較

方法難易度特徴
External Secrets OperatorWebhook 連携、宣言的管理
Init Container追加コンポーネント不要
Sidecarシークレットの自動更新

External Secrets Operator (ESO)

External Secrets Operator は Kubernetes でシークレットを外部プロバイダーから同期するための標準的なツールです。

ESO とは

ESO は以下の機能を提供します:

  • 外部シークレットストア(Vault、AWS Secrets Manager 等)との連携
  • Kubernetes Secret への自動同期
  • Webhook Provider による任意の HTTP API との連携

kvtool は Webhook Provider を通じて ESO と連携します。

ESO のインストール

helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
  -n external-secrets --create-namespace

kvtool のデプロイ

# kvtool-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kvtool
  namespace: infra
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kvtool
  template:
    metadata:
      labels:
        app: kvtool
    spec:
      containers:
        - name: kvtool
          image: your-registry/kvtool:latest
          args: ["store", "serve", "--port", "8080"]
          ports:
            - containerPort: 8080
          volumeMounts:
            - name: config
              mountPath: /app/.kvtool.yml
              subPath: .kvtool.yml
      volumes:
        - name: config
          configMap:
            name: kvtool-config
---
apiVersion: v1
kind: Service
metadata:
  name: kvtool
  namespace: infra
spec:
  selector:
    app: kvtool
  ports:
    - port: 8080
      targetPort: 8080

SecretStore の設定

ESO の Webhook Provider は URL テンプレーティングをサポートしています。remoteRef オブジェクトの値を URL に埋め込めます。

# secret-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: kvtool
spec:
  provider:
    webhook:
      url: "http://kvtool.infra.svc:8080/v1/namespaces/{{ .remoteRef.property }}/kv/{{ .remoteRef.key }}"
      result:
        jsonPath: "$"
      headers:
        Content-Type: application/json

URL テンプレート変数:

変数説明使用例
{{ .remoteRef.key }}ExternalSecret の remoteRef.keyvault/db/password
{{ .remoteRef.property }}ExternalSecret の remoteRef.propertyproduction

ExternalSecret の作成

# external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-app-secrets
  namespace: default
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: kvtool
    kind: ClusterSecretStore
  target:
    name: my-app-secrets  # 作成される K8s Secret の名前
    creationPolicy: Owner
  data:
    # 単一の値を取得
    - secretKey: db-password        # K8s Secret のキー
      remoteRef:
        key: vault/db/password      # kvtool の store/path
        property: production        # kvtool の namespace

    # JSON から特定のフィールドを抽出
    - secretKey: api-key
      remoteRef:
        key: vault/app/config
        property: production
      # JSONPath で値を抽出(オプション)
      # result:
      #   jsonPath: "$.api_key"

結果の確認

# ExternalSecret の状態確認
kubectl get externalsecret my-app-secrets

# 作成された Secret の確認
kubectl get secret my-app-secrets -o yaml

# 値の確認
kubectl get secret my-app-secrets -o jsonpath='{.data.db-password}' | base64 -d

Init Container パターン

ESO を使わずに、Init Container で直接 kvtool を実行する方法です。

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  initContainers:
    - name: load-secrets
      image: your-registry/kvtool:latest
      command:
        - sh
        - -c
        - |
          # 単一の値を取得
          kvtool store load -n production vault db/password > /secrets/db-password

          # .env 形式で複数の値を取得
          kvtool store load -n production local env/app.env > /secrets/.env
      volumeMounts:
        - name: secrets
          mountPath: /secrets
        - name: kvtool-config
          mountPath: /app/.kvtool.yml
          subPath: .kvtool.yml

  containers:
    - name: app
      image: my-app:latest
      volumeMounts:
        - name: secrets
          mountPath: /secrets
          readOnly: true
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-password-file
              key: password

  volumes:
    - name: secrets
      emptyDir:
        medium: Memory  # メモリ上に保持(セキュリティ向上)
    - name: kvtool-config
      configMap:
        name: kvtool-config

Sidecar パターン

シークレットを定期的に更新する場合は、Sidecar パターンを使用します。

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: app
      image: my-app:latest
      volumeMounts:
        - name: secrets
          mountPath: /secrets
          readOnly: true

    - name: secret-sync
      image: your-registry/kvtool:latest
      command:
        - sh
        - -c
        - |
          while true; do
            kvtool store load -n production vault db/password > /secrets/db-password
            sleep 300  # 5分ごとに更新
          done
      volumeMounts:
        - name: secrets
          mountPath: /secrets
        - name: kvtool-config
          mountPath: /app/.kvtool.yml
          subPath: .kvtool.yml

  volumes:
    - name: secrets
      emptyDir:
        medium: Memory
    - name: kvtool-config
      configMap:
        name: kvtool-config

セキュリティ考慮事項

ネットワークポリシー

kvtool サービスへのアクセスを制限します:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: kvtool-access
  namespace: infra
spec:
  podSelector:
    matchLabels:
      app: kvtool
  ingress:
    - from:
        # ESO からのアクセスのみ許可
        - namespaceSelector:
            matchLabels:
              name: external-secrets
        # または特定の namespace から
        - namespaceSelector:
            matchLabels:
              kvtool-access: "true"
      ports:
        - port: 8080

RBAC

kvtool の ConfigMap へのアクセスを制限します:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: kvtool-config-reader
  namespace: infra
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["kvtool-config"]
    verbs: ["get"]

トラブルシューティング

ESO が Secret を作成しない

# ExternalSecret のイベント確認
kubectl describe externalsecret my-app-secrets

# ESO のログ確認
kubectl logs -n external-secrets -l app.kubernetes.io/name=external-secrets

kvtool への接続エラー

# kvtool Pod の状態確認
kubectl get pods -n infra -l app=kvtool

# kvtool のログ確認
kubectl logs -n infra -l app=kvtool

# Service への疎通確認
kubectl run -it --rm debug --image=curlimages/curl -- \
  curl http://kvtool.infra.svc:8080/health

Tool コマンド

kvtool tool は UUID、タイムスタンプ、ランダム値、パスワードなどを生成するユーティリティコマンドです。

使用方法

kvtool tool <tool-name>

利用可能なツール

UUID

# UUID v7(時間ベース、ソート可能)
kvtool tool uuid7
# => 019c299f-c6ba-72dd-8d73-8bbe712f4cf0

# UUID v4(ランダム)
kvtool tool uuid4
# => 565ccde7-1f06-4c2c-92ff-6782568cd8a7

タイムスタンプ

# 現在時刻(UTC ISO8601)
kvtool tool now
# => 2024-01-15T10:30:00Z

# Unix タイムスタンプ(秒)
kvtool tool timestamp
# => 1705315800

ランダム値

# ランダムバイト(hex エンコード)
kvtool tool random/hex/16
# => a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6

# ランダムバイト(base64 エンコード)
kvtool tool random/base64/32
# => SGVsbG8gV29ybGQhIFRoaXMgaXMgYSB0ZXN0

バイト数は 1〜1024 の範囲で指定できます。

パスワード

# 16文字のパスワード
kvtool tool password
# => xK9#mP2$nQ7@wL4!

パスワードは以下の文字セットから生成されます:

  • 小文字: a-z
  • 大文字: A-Z
  • 数字: 0-9
  • 記号: !@#$%^&*

ユースケース

CI/CD でのシークレット生成

# デプロイ用の一意な ID を生成
DEPLOY_ID=$(kvtool tool uuid7)

# API キーを生成
API_KEY=$(kvtool tool random/hex/32)

スクリプトでの使用

#!/bin/bash

# ログファイル名にタイムスタンプを付与
LOG_FILE="app-$(kvtool tool timestamp).log"

# セッション ID を生成
SESSION_ID=$(kvtool tool uuid7)

環境変数の設定

export SECRET_KEY=$(kvtool tool random/base64/32)
export DB_PASSWORD=$(kvtool tool password)

ドライバー

このドキュメントは自動生成されています。手動で編集しないでください。

生成コマンド: go run scripts/gen-docs.go

LocalFsConfig

LocalFsConfig はローカルファイルシステムの設定

ファイル: local.go

パラメータ必須デフォルト説明
rootstring.ルートディレクトリ。このパスより上位には遡れない
transformstringjson読み込み時の変換方法(dotenv, json など)

設定例:

localfs:
  driver: local
  args:
    root: ./config
    transform: dotenv

S3FsConfig

S3FsConfig は S3 ファイルシステムの設定

ファイル: s3.go

パラメータ必須デフォルト説明
bucketstring-S3 バケット名
regionstring-AWS リージョン
access_key_idstring-AWS アクセスキー ID
secret_access_keystring-AWS シークレットアクセスキー
session_tokenstring-AWS セッショントークン(一時的な認証情報使用時)
endpointstring-カスタムエンドポイント(MinIO など S3 互換ストレージ用)
use_path_styleboolfalseパススタイルアクセスを使用(S3 互換ストレージで必要な場合あり)
transformstring-読み込み時の変換方法(dotenv, json など)

設定例:

s3fs:
  driver: s3
  args:
    bucket: my-config-bucket
    region: ap-northeast-1
    access_key_id: AKIAIOSFODNN7EXAMPLE
    secret_access_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
    session_token: FwoGZXIvYXdzE...
    endpoint: http://localhost:9000
    use_path_style: true
    transform: dotenv

VaultConfig

VaultConfig は HashiCorp Vault ファイルシステムの設定

ファイル: vault.go

パラメータ必須デフォルト説明
addrstring-Vault サーバーのアドレス
tokenstring-Vault 認証トークン
namespacestring-Vault 名前空間(Enterprise 機能)
mountstringsecretKV シークレットエンジンのマウントパス
kv_verint2KV シークレットエンジンのバージョン(現在は 2 のみサポート)
versionint0取得するシークレットのバージョン(0 = 最新)
fieldstring-取得する特定のフィールド名(省略時は全フィールド)
prettyboolfalseJSON 出力を整形するか

設定例:

vault:
  driver: vault
  args:
    addr: http://localhost:8200
    token: root
    namespace: admin
    mount: secret

RestFsConfig

RestFsConfig は REST ファイルシステムの設定

ファイル: rest.go

パラメータ必須デフォルト説明
base_urlstring-ベース URL
auth_typestring-認証タイプ(bearer, basic)
tokenstring-Bearer トークン
token_filestring-Bearer トークンファイルパス
usernamestring-Basic 認証ユーザー名
passwordstring-Basic 認証パスワード
ca_filestring-CA 証明書ファイルパス
insecureboolfalseTLS 証明書検証をスキップ

設定例:

restfs:
  driver: rest
  args:
    base_url: https://api.example.com
    auth_type: bearer
    token_file: /var/run/secrets/token

DbFsConfig

DbFsConfig はデータベースファイルシステムの設定

ファイル: db.go

パラメータ必須デフォルト説明
connection_stringstring-データベース接続文字列
driverstring-データベースドライバー(postgres, mysql, sqlite)。省略時は接続文字列から自動判定
querystring-SQL クエリ。{key} と {namespace} がプレースホルダーとして使用可能
namespacestringdefaultデフォルトの namespace 値

設定例:

dbfs:
  driver: db
  args:
    connection_string: postgres://user:pass@localhost/db
    query: SELECT value FROM config WHERE key = {key}
    namespace: production

NatsFsConfig

NatsFsConfig は NATS JetStream KV ファイルシステムの設定

ファイル: nats.go

パラメータ必須デフォルト説明
urlstring-NATS サーバーの接続 URL
bucketstring-JetStream KV バケット名
tokenstring-NATS 認証トークン
userstring-NATS ユーザー名
passwordstring-NATS パスワード
creds_filestring-NATS 認証情報ファイルパス

設定例:

natsfs:
  driver: nats
  args:
    url: nats://localhost:4222
    bucket: config

RedisFsConfig

RedisFsConfig は Redis ファイルシステムの設定

ファイル: redis.go

パラメータ必須デフォルト説明
addrstring-Redis サーバーのアドレス
passwordstring-Redis 認証パスワード
dbint0Redis データベース番号
prefixstring-キーのプレフィックス

設定例:

redisfs:
  driver: redis
  args:
    addr: localhost:6379
    db: 0
    prefix: config:

GitFsConfig

GitFsConfig は Git ファイルシステムの設定

ファイル: git.go

パラメータ必須デフォルト説明
urlstring-Git リポジトリ URL
refstringmainブランチまたはタグ名

設定例:

gitfs:
  driver: git
  args:
    url: https://github.com/example/config.git
    ref: main

EnvFsConfig

EnvFsConfig は環境変数ファイルシステムの設定

ファイル: env.go

パラメータ必須デフォルト説明
include[]string-許可する環境変数キーのリスト。未指定で全許可
exclude[]string-除外する環境変数キーのリスト。include より優先

設定例:

envfs:
  driver: env
  args:
    include: APP_NAME,DATABASE_URL
    exclude: SECRET_KEY,PASSWORD

BitwardenFsConfig

BitwardenFsConfig は Bitwarden Secrets Manager ファイルシステムの設定

ファイル: bitwarden.go

パラメータ必須デフォルト説明
access_tokenstring-Bitwarden アクセストークン
organization_idstring-Bitwarden 組織 ID
project_idstring-フィルタ対象のプロジェクト ID

設定例:

bitwardenfs:
  driver: bitwarden
  args:
    access_token: 0.48c78342-...
    organization_id: a1b2c3d4-...
    project_id: e325ea69-...

ToolFsConfig

ToolFsConfig は Tool ファイルシステムの設定 設定パラメータは不要です。 利用可能なパス: uuid7, uuid4, now, timestamp, random/hex/{bytes}, random/base64/{bytes}, password

ファイル: tool.go


.env ファイル仕様

kvtool における .env ファイルの読み書き仕様を定義します。

設計方針

  • Node.js dotenv ベース: 最も広く使われている実装に準拠
  • 行末コメント対応: 実用性のために追加
  • セキュリティ優先: 変数展開・コマンド置換は非サポート
  • 読み取り専用: 書き込み操作は提供しない(下記参照)

なぜ書き込み操作を提供しないか

kvtool は設定の読み取り専用ツールです。書き込み操作(Write)は意図的にサポートしません。

セキュリティ上の理由:

# 例: もし HCL への書き込みが可能だった場合
kvtool convert env:// --to hcl > terraform.tfvars

# 環境変数に含まれる機密情報(API キー、パスワード等)が
# 意図せずファイルに書き出され、Git にコミットされるリスク

設計意図:

  • 設定の「取得」と「変換」に特化
  • 書き込みは既存のツール(echo, cat, エディタ)で十分
  • 機密情報の意図しない永続化を防止

機能対応表

機能対応備考
ダブルクォートエスケープ処理あり
シングルクォートリテラル(エスケープなし)
エスケープ \n改行
エスケープ \rCR(Windows互換)
エスケープ \"クォート内に " を含めるため
エスケープ \tNode.js 互換で非対応
エスケープ \\Node.js 互換で非対応
行末コメントKEY=value # comment
空のキー =valueエラー(POSIX 準拠)
数字で始まるキーエラー(POSIX 準拠)
キー名検証[a-zA-Z_][a-zA-Z0-9_]* (POSIX 準拠)

| export プレフィックス | ❌ | サポートしない | | 変数展開 ${VAR} | ❌ | リテラル扱い | | コマンド置換 $(cmd) | ❌ | リテラル扱い | | 複数行 (heredoc) | ❌ | サポートしない | | UTF-8 BOM | ✅ | 自動スキップ | | NULL 文字 \x00 | ❌ | エラー(セキュリティ) |

基本構文

キー名のルール

キー名は POSIX シェル変数名 に準拠します:

  • パターン: [a-zA-Z_][a-zA-Z0-9_]*
  • 英字(a-z, A-Z)またはアンダースコア(_)で開始
  • 以降は英字、数字、アンダースコアのみ
# ✅ 有効なキー名
FOO=bar
_PRIVATE=value
MY_VAR_123=value

# ❌ 無効なキー名(エラーになる)
1KEY=value           # 数字で始まる
MY-VAR=value         # ハイフンを含む
MY.VAR=value         # ドットを含む
キー=value            # 非ASCII文字

KEY=VALUE 形式

# 基本形式
KEY=value

# 空の値
EMPTY_KEY=

# 値に = を含む
DATABASE_URL=postgres://user:pass@host:5432/db

クォート

# ダブルクォート: エスケープシーケンスが有効
MESSAGE="Hello\nWorld"        # Hello + 改行 + World

# シングルクォート: リテラル(エスケープなし)
MESSAGE='Hello\nWorld'        # Hello\nWorld(そのまま)

# クォートなし
MESSAGE=HelloWorld

エスケープシーケンス

ダブルクォート内でのみ有効:

シーケンス変換後
\n改行 (LF)
\rキャリッジリターン (CR)
\"ダブルクォート

非対応のエスケープ:

  • \t → そのまま \t として扱う
  • \\ → そのまま \\ として扱う

コメント

# 行頭コメント
KEY=value

KEY=value # 行末コメント(クォート外)

KEY="value # not a comment"  # クォート内は値の一部

非サポート機能

以下の機能は意図的にサポートしません:

export プレフィックス

# ❌ サポートしない
export KEY=value

# ✅ 代わりにこう書く
KEY=value

理由: シンプルさを優先。bash での source .env が必要な場合は別途対応。

変数展開

# ❌ 展開しない(リテラル扱い)
BASE_URL=http://localhost
API_URL=${BASE_URL}/api    # → "${BASE_URL}/api" という文字列

# ✅ 展開が必要な場合は呼び出し側で処理

理由: セキュリティと予測可能性を優先。意図しない値の漏洩を防止。

コマンド置換

# ❌ 実行しない(リテラル扱い)
DATE=$(date +%Y-%m-%d)     # → "$(date +%Y-%m-%d)" という文字列

理由: セキュリティを優先。任意のコマンド実行を防止。

複数行値 (heredoc)

# ❌ サポートしない
PRIVATE_KEY="""
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
"""

# ✅ 代わりにエスケープシーケンスを使用
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"

理由: シンプルさを優先。複雑な値は JSON や Vault を推奨。

他ツールとの互換性

ツール互換性差異
Node.js dotenvほぼ同等行末コメント追加
docker-compose部分的export, 変数展開なし
Python python-dotenv部分的変数展開なし

Node.js dotenv との比較

# 両方で同じ動作
KEY="hello\nworld"           # → hello + 改行 + world

# kvtool のみ対応
KEY=value # comment          # → value(行末コメント)

# どちらも非対応(リテラル扱い)
KEY=${OTHER_KEY}             # → "${OTHER_KEY}"

使用例

.env ファイルの読み込み

# ローカルファイルから
kvtool file load .env --transform dotenv

# ストア経由
kvtool store load config/.env

環境変数の出力

# 環境変数を .env 形式で出力
kvtool file load env:// --format raw

エンコーディング

  • UTF-8 のみ: BOM 付きファイルも自動的に処理(BOM をスキップ)
  • 改行コード: LF (\n) を推奨、CRLF (\r\n) も許容

アーキテクチャ

kvtool のアーキテクチャ概要です。

レイヤー構造

Presentation (CLI)
    ↓
Service
    ↓
Infrastructure (Filesystem)
  • Presentation: Cobra を使った CLI インターフェース (cmd/)
  • Service: ビジネスロジック
  • Infrastructure: ファイルシステムドライバー (pkg/filesystems/)

統一インターフェース

全てのファイルシステムは以下のインターフェースを実装します (p../pkg/filesystems/core.go):

type Filesystem interface {
    GetFile(path string) (File, error)
}

type File interface {
    LoadAsJson() (any, error)
    OpenReader() (io.ReadCloser, error)
}

ファイルシステムドライバー

実装済みドライバー

ドライバーの作成

FilesystemFactory がドライバー名から適切なファイルシステムインスタンスを生成します (p../pkg/filesystems/factory.go)。

Transform(変換機能)

ファイルを読み込む際に、特定の形式(.env など)を JSON に変換する機能です。

実装済み Transform

テスト戦略

  • 統合テスト: p../pkg/filesystems/integration_test.go で全ファイルシステムの共通動作を検証
  • テーブル駆動テスト: 全てのテストはテーブル駆動テストで記述
  • Docker 統合: Vault などの外部サービスは Docker で起動してテスト

詳細は CLAUDE.md を参照してください。

開発ガイド

kvtool の開発に参加する方向けのガイドです。

開発環境のセットアップ

必要なツール

  • Go 1.21 以上
  • Make
  • Docker(統合テスト用)

テスト実行

# 全テスト実行
make test

# Vault含む統合テスト
make test-full

テスト用サービスの起動

# 全サービス起動(Vault + MinIO)
make services-up
# Vault: http://localhost:8200 (token: root)
# MinIO: http://localhost:9000 (user: minioadmin)

# 全サービス停止
make services-down

# ログ確認
make services-logs

コード規約

詳細は CLAUDE.md を参照してください。

テスト規約

  • テーブル駆動テスト: 全てのテストはテーブル駆動テストで記述
  • 日本語: テスト名、コメント、エラーメッセージは日本語で記述

セキュリティ規約

  • パストラバーサル防止: 全てのファイルシステムは root より上位に遡れないようにする
  • 相対パスのみ: 絶対パス(/etc/passwd)やチルダパス(~/config)は拒否

ドキュメント生成

# APIリファレンスを自動生成
make doc-gen

コード内のコメントと構造体タグから docs/api-reference.md が自動生成されます。

コード品質

# 静的解析
make lint

# コードフォーマット
make format

# 品質分析(カバレッジ + 複雑度)
make test-quality

# 開発ツールのインストール(golangci-lint)
make setup-tools

新しいファイルシステムドライバーの追加

  1. 仕様策定: do../pkg/filesystems/{driver}.md を作成
  2. 構造体定義: 設定構造体を定義(doc, required, default, example タグを含む)
  3. インターフェース実装: FilesystemFile インターフェースを実装
  4. テスト: filesystems/integration_test.go にテストケースを追加
  5. ドキュメント生成: make doc-gen を実行

詳細は CLAUDE.md の「開発ワークフロー」セクションを参照してください。