ブログ

AIにデータベースを削除された:7分間の復旧計画

Markdown

データを削除された後にすべきこと:エージェントを停止し、インシデントを記録し、git reflog、PITR、スナップショット、ファイル復旧を使います。具体例と防止策。

記事の全文を読む

重要なポイント

AIエージェントがデータベース、ファイル、あるいは本番環境全体を削除してしまいました。最初の7分で、データを復旧できるか、永久に失うかが決まります。この手順書では、各分に実行する具体的なコマンドを示します。まずエージェントを停止し、次にインシデントを記録してから復旧します。慌てず、チェックリストに従ってください。

Ailootの利用に関する実用情報はドキュメントで確認できます。

最初の1分にすること:停止、慌てない

重要なポイント

破壊的な書き手を直ちに停止し、エージェント停止後も続く子も確認します。再起動したり、削除ファイルを保持する無関係のプロセスを閉じたりしないでください。上書きやSSDのdiscard/TRIMで復旧不能になる場合もあり、成功は保証されません。壊れたコピーを変更する前に、証拠と残存データを別の検証済み機器へ保全します。

同じことが起きたら、最初にすることはエージェントを停止することです。自分で操作を取り消そうとしたり、「すべて復元して」と指示したり、新しいプロンプトを実行したりしないでください。エージェントがその後に行う操作によって、データがまだ残っている可能性のあるディスクブロックが上書きされることがあります。

  1. エージェントのターミナルを閉じる

    Ctrl+Cで危険なコマンドを直ちに中断し、削除の完了は待ちません。子コマンド、遠隔ジョブ、DBクエリは残る場合があるため、実際の書き手を特定して停止・キャンセルし、破壊が続けば終了を強めます。復旧に役立つ開いたファイルを持つ無関係プロセスは残します。

  2. ノートパソコンを再起動しない

    削除データが残る場合はありますが、保存方式、DB挙動、SSDのdiscard/TRIM、後の書き込み次第です。対象ボリュームへの書き込みを減らし、バイトが必ず残るとは考えません。

  3. 開いているプロセスを終了しない

    無関係のプロセスが削除ファイルを開いていれば、ディスクリプターから回収できる場合があるため保持します。ただし破壊的な書き手は先に止めます。開いたハンドルを守るために削除を続行しないでください。

  4. 画面録画を開始する

    正確な時刻、対象パス、画面のコマンド出力を記録します。録画やログは壊れたボリュームでなく、別の検証済み機器へ保存します。復旧前に追跡・未追跡・無視ファイルとGit情報もそこへ保全します。

次は復旧のための6分間です。始めましょう。

2~3分目:何が起きたかを正確に記録する

重要なポイント

復旧の前に状況を診断してください。「何が、誰によって、いつ、どのコマンドで削除されたのか」を正確に把握しなければ、手探りでの復旧になり、まだ無事なデータまで壊すおそれがあります。新しいターミナルウィンドウを開き(エージェントが動いているものとは別のウィンドウ)、git status、git log --since、ps aux | grep claudeを実行します。すぐに~/.claude/projects/フォルダーをコピーしてください。エージェントのセッションログ全体が含まれており、復旧に役立つ可能性があります。

新しいターミナルウィンドウを開きます。エージェントが動いているウィンドウではなく、別のウィンドウです。次の3つのコマンドを順に実行します。

1. gitリポジトリで何が変わったかを正確に記録する:

git status
git log --since="30 minutes ago" --all --oneline

削除された追跡ファイルはインデックスやコミットに残る場合があります。復元前に、未追跡・無視ファイルも含む現在の作業を保全します。その場のrestoreは残存編集を上書きしうるため、別コピーで調べて復旧してください。

2. 実行中のエージェントプロセスを確認する:

ps aux | grep -i claude
ps aux | grep -i cursor

名前の一致だけで決めず、危険な書き込みを続ける実プロセスを特定して止め、停止を確認します。有用なファイルを持つ無関係なものは残します。通常の停止が失敗し破壊が続くなら、強制終了が必要な場合もあります。

3. エージェントのセッションログを保存する:

Claude CodeはJSONLセッションを~/.claude/projects/に保存します。Cursorの場合は~/.cursor/です。今すぐコピーを作成してください。

cp -r ~/.claude/projects /external/incident-evidence/claude-projects

/external/incident-evidence/は、別の検証済みマウント機器上にある既存の証拠保存先へ置き換えます。対象ボリュームや既存証拠の上へ書かないでください。このフォルダーには、エージェントとの会話がそのまま含まれています。実行したコマンド、返答、実行順序が記録されています。インシデント調査の主な資料となり、文脈から削除されたファイルを復旧できる可能性もあります(手順4を参照)。

4分目:git reflog――最も有望な手段

重要なポイント

reflogはブランチ履歴から消えたコミットを探せますが、未コミットの作業ファイルは保存しません。残存データとGit情報を別機器へ保全した後、そのコピーで確認し、検証したコミットを別worktreeへ復旧します。壊れた作業ツリーのresetから始めないでください。

git reflogは保持されているローカル参照の移動を記録します。期限や整理は変わり、到達不能オブジェクトは消えうるため、全過去コミットの保証付き保管庫ではありません。以下は別機器へ保全したリポジトリから実行します。

手順1. ログを表示する:

git reflog --all

次のような一覧が表示されます。

1a2b3c4 HEAD@{0}: reset: moving to HEAD~3
9z8y7x6 HEAD@{1}: commit: working version of the dashboard
5w4v3u2 HEAD@{2}: commit: added admin panel

エージェントが損害を起こす前の時点を探してください。各エントリーは、その時点のリポジトリ状態の「スナップショット」です。

手順2. 別の作業ツリーへ復旧する:

リポジトリと残存作業を別の機器へコピーしてから、そこで実行します。reflogで確認した正確なSHAを指定し、未使用のブランチと宛先を選びます。

RECOVERY_COMMIT='replace-with-verified-commit-sha'
git worktree add -b recovery-check ../recovery-check "$RECOVERY_COMMIT"

../recovery-checkを調べ、戻す前に保全済み未コミットファイルと比較します。選択コミットを別に展開し、壊れた作業ツリーはresetしません。worktreeはコピー元のGitオブジェクトを共有するため、復旧作業場であって独立バックアップではありません。公式git-worktree資料も参照してください。

reflogも整理されていた場合(エージェントがgit gcを実行した場合):

git fsck --lost-found

このコマンドは、どのブランチにも紐づいていない「失われた」コミットやブロブ(ファイルオブジェクト)を探します。.git/lost-found/commit/と.git/lost-found/other/に表示されます。そこからファイルを復元するには、次のようにします。

git cat-file -p <SHA1-blob> > recovered-file.ext

git reflogで復旧できない場合:

  • ファイルがコミットもスタッシュもされず、作業ツリーにしかなかった → reflogでは見つけられません
  • 90日以上が経過し(標準のgc.reflogExpire)、git gcが実行された
  • リポジトリ全体(rm -rf .git)が削除された → Time Machineまたはスナップショットが必要です

5分目:バックアップからデータベースを復元する

重要なポイント

PostgreSQLのポイントインタイムリカバリ(PITR)では、7~35日間の範囲内の任意の秒時点にデータベースを復元できます。Yandex Cloud Managed Postgres、AWS RDS、Supabase Proで利用でき、Coolifyではローカルのpg_dump cronジョブでも実現できます。PITRはインシデント発生前に有効化されている必要があります。後から有効化することはできません。必ず新しいインスタンスに復元し、稼働中のデータベースには上書きしないでください。ロールバックでさらにデータを失うのを防げます。PITRが有効でなければ、最新のスナップショットを使います。

データベースがDROP DATABASE、DROP TABLE、またはDELETE FROM ... WHERE ...(WHERE句なし)で削除されました。どうすればよいでしょうか。

手順1. プロバイダーがPITRを提供しているか確認する:

プロバイダーPITRの期間復元コマンド
AWS RDS1~35日(設定可能)aws rds restore-db-instance-to-point-in-time --restore-time 2026-05-28T14:30:00Z
Yandex Cloud Managed Postgres7日(既定)コンソールでバックアップから復元し、時刻を指定
Supabase Pro7日(Pro+のみ)UI:Database → Backups → Restore
Coolify + ローカルのpg_dumpcronのスケジュールに従うpg_restore -d new_db backup.dump

手順2. 古いインスタンスではなく、新しいインスタンスに復元する:

稼働中のデータベースに上書きしてはいけません。バックアップから新しいインスタンスを作り、アプリケーションの接続先を切り替えます。問題が起きたら、DNSを元に戻せます。

手順3. PITRが有効でなければ、最新のスナップショットを使う:

# AWS RDS、スナップショット一覧:
aws rds describe-db-snapshots --db-instance-identifier my-db

# スナップショットから復元:
aws rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier my-db-restored \
  --db-snapshot-identifier rds:my-db-2026-05-28-12-00

Supabaseの注意点として、2026年に多くの人が気づいたことがあります。PITRを有効にすると、日次バックアップの作成が停止します。直感に反する動作で、バックアップがまったくない状態になることもあります。Dashboard → Database → Backupsを開いて、PITRが有効で、復元できるスナップショットもあることを確認してください。

6分目:Time Machine、lsof、ディスクスナップショット

重要なポイント

ファイルがgitやデータベースではなくディスクから削除された場合、実用的な復旧方法は3つあります。macOSでは、(ノートパソコンがTime Capsuleまたは外付けドライブに接続されていたなら)1時間ごとのスナップショットをTime Machineで使います。Linuxではsudo lsof | grep deletedを実行すると、プロセスがまだ開いているファイルを取り出せる可能性があります(成功率95%)。/proc/PID/fd/N経由でコピーします。重要なケースでは、ddでパーティション全体のスナップショットを作成してから、extundeleteまたはphotorecで復旧します。

macOS――Time Machine:

# スナップショット一覧:
tmutil listbackups

# 最新のバックアップからファイルを復元:
tmutil restore /Volumes/Backup/.../my-project/file.tsx ~/projects/my-project/file.tsx

Time Machineは1時間ごとにスナップショットを作成し、1時間ごとのスナップショットを1日間、日ごとのスナップショットを1週間、週ごとのスナップショットを無期限に保持します。ノートパソコンがTime Capsuleまたは外付けドライブに接続されていれば、プロジェクトを1時間単位で復元できます。

Linux――ファイルは削除されたが、プロセスがまだ開いている場合:

# プロセスがまだ開いている「削除済み」ファイルをすべて探す:
sudo lsof | grep deleted

# 次のような行が表示されます:
# node 12345 user 24w REG 8,1 4096 1234567 /path/to/deleted/file.txt (deleted)

# /proc/PID/fd/Nからファイルを直接コピー:
sudo cp /proc/12345/fd/24 ~/recovered-file.txt

プロセスを終了していなければ、成功率は約95%です。ext4、xfs、btrfsで動作します。

macOS――lsofを使う方法:

lsof | grep deleted

ファイルをコピーするのは難しく(/proc/がないため)、lldbを使います。

lldb -p 12345
(lldb) memory read --outfile recovered.txt --binary 0xADDRESS 0xSIZE

ディスク全体のスナップショット(Linux、最後の手段):

sudo dd if=/dev/sda1 of=/external/disk-image.img bs=4M status=progress

パーティション全体をイメージとして保存します。その後、extundeleteまたはphotorecで復旧します。復旧の可能性は、削除後にディスクへどれだけ書き込んだかによって異なります。何も書き込まれていなければ80~90%です。すでに1時間稼働していれば20~30%です。

7分目:バックアップがない場合

重要なポイント

バックアップがなく、git reflogも空で、ディスクはすでに新しいデータで上書きされています。最後の手段は2つあります。プロバイダーのサポートに問い合わせる(AWS、Yandex Cloud、Google Cloudは、内部ストレージから定期的にデータを復旧することがあります)。またはS3のバージョニングで復元する(有効ならすべてのバージョンが物理的に残っています)。

選択肢1――プロバイダーのサポート:

AWS、Yandex Cloud、Google Cloud、DigitalOceanは、顧客側で障害が起きた後でも、内部ストレージから定期的にデータを復旧することがあります。特に有料プランで可能性があります。サポートチケットを作成し、インシデントが起きた正確な時刻を伝え、状態のスクリーンショットを添えてください。プロバイダー側で自動バックアップを保持している大手サービスでは、これが最もよく成功します。成功率は40~60%、回答までの期間は24~72時間です。

選択肢2――S3バージョニング:

ファイルがS3(またはYandex Object Storage)にあり、バージョニングが有効なら、すべてのバージョンが物理的に存在しています。「削除済み」とマークされているだけです。

# AWS――バージョン一覧:
aws s3api list-object-versions --bucket my-bucket --prefix files/

# バージョンを復元:
aws s3api copy-object \
  --bucket my-bucket \
  --copy-source my-bucket/files/data.json?versionId=ABC123 \
  --key files/data.json

重要な注意点: Claude Codeはセッションを永久に保持しません。古いJSONLファイルは数週間後に自動削除されます。障害に備えて1か月分のJSONL履歴を確保するため、週に1度、簡単なcronジョブで~/.claude/projects/を外付けドライブにコピーしてください。

# crontabに、毎週日曜日の03:00に実行する設定を追加:
0 3 * * 0 rsync -av ~/.claude/projects/ /external/claude-history-backup/

無料で利用でき、「すべて消えて、バックアップもなく、reflogも空」というときに役立ちます。

二度と起こさないための7つのルール

重要なポイント

AIエージェントにデータベースやコードを削除させないための7つのルールです。破壊的な操作の前にPlan Modeを使う。テスト済みフックで選んだ危険な文字列を検出し、強制する権限制限を併用する。エージェントには読み取り専用のデータベースユーザーを割り当てる。ファイル編集のたびに自動コミットする。実験には隔離されたサンドボックスフォルダーを使う。resticでディスクスナップショットを1時間ごとに作成する。JSONLセッションを外付けドライブにアーカイブする。

1. 破壊的な操作の前にPlan Modeを使う。 Claude Codeでは次のようにします。

claude --plan

エージェントは操作する前に計画を作ります。計画を読んで、DROP TABLE usersがあればキャンセルできます。Plan Modeを使わないと、エージェントは自分で判断し、数ミリ秒で実行します。詳しくはClaude CodeのPlan Modeガイドをご覧ください。

2. フックは追加の検知線。 イベントJSONをstdinで読み、認識した高リスク操作にexit 2を返す、テスト済みPreToolUseを使います。以下の完全な例はBash、Write、Editを扱いますが、間接操作は見逃しえます。安全境界ではないため、DB権限とsandboxを制限します。

設定の詳細はClaude Codeの2026年版Hooksガイドをご覧ください。

3. エージェントには読み取り専用のDBユーザーを割り当てる。 DROP、TRUNCATE、DELETEの権限を持たない専用ユーザーを作成します。

CREATE USER claude_readonly WITH PASSWORD 'xxx';
GRANT CONNECT ON DATABASE myapp TO claude_readonly;
GRANT USAGE ON SCHEMA public TO claude_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO claude_readonly;

エージェントが本当に書き込む必要があるときは、手動で別のユーザーに切り替えます。読み取り専用ユーザーで、日常的なタスク(データ分析、レポート、ダッシュボード)には十分対応できます。

4. 確認済みの変更をGitに記録する。 .claude/settings.jsonのPostToolUseフックを使うと、WriteとEditの後に確認のメッセージを表示できます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "printf '%s\\n' '差分を確認し、合意したファイルだけを手動でコミットしてください。' >&2"
          }
        ]
      }
    ]
  }
}

この例はメッセージを表示するだけで、ファイルのステージングやコミットは行いません。差分を確認し、合意したファイルだけを手動でコミットしてください。reflogは未コミットの変更を保存しません。

5. 実験用のサンドボックスプロジェクトを用意する。 実際の顧客データを含むプロジェクトでは、エージェントを動かさないでください。プロジェクトのコピーと合成データを使い、別の~/sandbox-projects/フォルダーを作成します。新しいプロンプト、エージェント、MCPサーバーはそこで試します。問題が起きても被害はありません。

6. resticでディスクスナップショットを1時間ごとに作成する。

# インストール:
brew install restic  # macOS
sudo apt install restic  # Linux

# リポジトリを作成:
restic init --repo /external/backups

# 現在のプロジェクトをバックアップ:
restic --repo /external/backups backup ~/projects

# cronを1時間ごとに実行するよう設定:
crontab -e
# 次を追加:
0 * * * * restic --repo /external/backups backup ~/projects

7日分の1時間ごとのバックアップと、4週間分の日次バックアップを保持できます。過去の任意の時点に戻せます。無料で、設定にかかる時間は10分です。

7. Claude CodeのJSONLセッションをアーカイブする。 上でも触れましたが、別のルールとして繰り返します。毎週、~/.claude/projects/を外付けドライブにrsyncするようスケジュールしてください。無料で、crontabに1行追加するだけです。

危険なコマンドをブロックする、そのまま使えるフック

重要なポイント

以下は選んだBash文字列と機密Write/EditパスのPreToolUse検知例です。stdinのJSONを読み、一致すればexit 2を返します。間接・難読化操作を見逃しうるため、sandboxと制限した認証の補助です。既存設定へ統合し、使用前に挙動を試してください。

既存のフックを置き換えず、.claude/settings.jsonへ統合します。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/check-risk.py\""
          }
        ]
      }
    ]
  }
}

次を.claude/hooks/check-risk.pyへ保存します。

import json
import re
import sys
from pathlib import Path

try:
    event = json.load(sys.stdin)
    tool = event["tool_name"]
    data = event["tool_input"]
    blocked = False
    if tool == "Bash":
        command = data["command"]
        blocked = bool(re.search(
            r"drop\s+(database|table)|truncate\s+table|"
            r"delete\s+from.*where\s+1|rm\s+-rf|"
            r"git\s+reset\s+--hard|git\s+push.*(--force|-f)|"
            r"chmod\s+-R\s+777",
            command, re.IGNORECASE | re.DOTALL))
    elif tool in ("Write", "Edit"):
        path = Path(data["file_path"]).resolve()
        blocked = (".git" in path.parts or path.name.startswith(
            (".env", "credentials", "secrets")))
except (ValueError, KeyError, TypeError, OSError):
    print("BLOCKED: invalid hook input", file=sys.stderr)
    sys.exit(2)

if blocked:
    print("BLOCKED: high-risk command or sensitive path", file=sys.stderr)
    sys.exit(2)
sys.exit(0)

stdinの公式JSONからtool_input.commandかtool_input.file_pathを調べ、一致すれば2を返します。WriteとEditの両方が対象です。無害な許可例と拒否例で試し、Pythonと設定スクリプトが起動することを確認してください。

字面の検知なので、引数順、スクリプト、別名、難読化、他ツール、間接書き込みを見逃し、無害な引用を拒否する場合もあります。OS・DB権限の付与・取消はしません。本番認証を環境外へ置き、読み取り専用DBロールと強制sandboxを使い、フック設定を変更から守ります。公式の入力・終了コードも確認します。

意図的な例外は確認したうえで、適切に制限したアカウントでエージェント外から実行します。シェル環境変数は文書化されたフック無効化インターフェースではなく、通常ターミナルもフックの対象外です。

AIに権限を与えるとき、与えてはいけないとき

重要なポイント

実データのあるプロジェクトで、エージェントに--dangerously-skip-permissionsフラグを絶対に与えないでください。このYOLOモードではすべての保護が無効になり、エージェントは確認なしであらゆるBashコマンドを実行してファイルに書き込みます。起動したサブエージェントもYOLOを引き継ぎ、上書きする方法はありません。YOLOを使うのは、サンドボックスフォルダーで新しいMCPサーバーやプロンプトをダミーデータで試す場合だけです。それ以外では、.claude/settings.jsonで明示的なallowとdenyルールを設定し、必要な権限だけを与えてください。

Anthropicは--dangerously-skip-permissionsフラグ(YOLOモードとも呼ばれます)を「信頼できる環境」の選択肢として説明しています。2026年の現実では、これは重大な障害に直結する道です。エージェントに確認させないと、次の操作を実行します。

  • 確認なしであらゆるBashコマンドを実行する
  • 尋ねずにファイルを書き込む
  • サブエージェントを呼び出す。そのサブエージェントもYOLOを引き継ぎ、無効にできない

3つ目が最も厄介です。メインエージェントをYOLOで起動すると、サブエージェントもYOLOになり、上書きできません。1つのプロンプトで10個のエージェントが同時に連鎖的に動き、システム上で何でもできる状態になり得ます。

ルール:

状況YOLOを使ってよいか
サンドボックスフォルダーで新しいMCPサーバーを試す✅ はい
ダミーデータで新しいプロンプトを試す✅ はい
実際のプロジェクトファイルを扱う❌ いいえ
データベースに触れるコマンド❌ 絶対に不可
本番環境へのデプロイ❌ 絶対に不可
インフラ(AWS、Yandex Cloud)の管理❌ 絶対に不可

代替策――必要な権限だけを設定する。 .claude/settings.jsonでは次のようにします。

{
  "permissions": {
    "allow": [
      "Bash(git status:*)",
      "Bash(git log:*)",
      "Bash(npm run build:*)",
      "Read(*)",
      "Edit(src/**)"
    ],
    "deny": [
      "Bash(rm:*)",
      "Bash(git push:*)",
      "Edit(.env)",
      "Edit(prisma/migrations/*)"
    ]
  }
}

エージェントには、許可されている操作と禁止されている操作がわかります。毎回「はい」か「いいえ」をクリックしなくても、素早く作業できます。管理するのは権限の範囲です。

次に読むもの:ブックマーク用の7分間チェックリスト

重要なポイント

今すぐこのガイドをブックマークしてください。障害が起きたらページを開き、10項目のチェックリストを順番に実行します。慌てる時間を30分節約し、復旧できるかもしれないデータを誤って上書きするのを防げます。復旧後に戻ってきて、同じ障害が翌週また起きないよう、7つの予防ルールを設定してください。

チェックリスト:「AIに削除された――どうすればいい?」

  1. 実際の破壊的な書き手を直ちに停止し、子や遠隔処理の停止も確認
  2. 再起動せず、削除ファイルを保持する無関係のプロセスを残す
  3. 発生時刻とコマンドの証拠を別の検証済み機器へ記録
  4. 残存ファイル、Git情報、該当セッションログを同じ別機器へコピー
  5. 保全したコピーでgit reflog --allを確認し、検証したコミットを別worktreeへ復旧
  6. gitで復旧できなければgit fsck --lost-found
  7. データベース:PITR(AWS/Yandex/Supabase)または最新のスナップショット
  8. ファイル:MacではTime Machine、Linuxではlsof
  9. 最後の手段:プロバイダーサポート + S3バージョニング
  10. 復旧後、このガイドの7つの予防ルールを設定する

次に――体系的な保護:

Ailootを開く。

詳細を見る

AIにデータベースを削除された:7分間の復旧計画:よくある質問

エージェントの実行中コマンドが終わるまで待ちますか?

いいえ。破壊的な書き手を直ちに止め、子プロセス、遠隔ジョブ、DB操作も停止したか確認します。消えたファイルを保持している無関係のプロセスは残します。エージェント停止だけでは、別の書き手が動いている場合があります。

復旧をgit reset --hardから始めてよいですか?

先に追跡・未追跡・無視ファイルとGit情報を別の機器へ保全します。コピーしたリポジトリで調べ、確認したコミットを別worktreeへ復旧してください。その場のhard resetは生き残った未コミット編集を失わせえます。

すべての記事