dbcli 完整說明文件

給人類開發者與 AI 代理的安全優先資料庫 CLI —— SQL、MongoDB、Redis、Elasticsearch 共用同一套介面。

permission、blacklist、dry-run 與 agent skill 是縱深防禦,不能取代資料庫授權。自主 agent 只能取得最小權限的資料庫憑證;若一個 process 能修改自己的設定,它也能提高 dbcli 宣告的 permission,或改用其他 client。

核心理念與安全性

dbcli 以「安全第一」為核心,透過多層保護機制防止資料意外洩漏或損壞:

權限守衛 (Permission Guard)

四層存取控制(從 query-only 到 admin),嚴格把關每一項操作;語句以實際行為判定而非開頭關鍵字 —— 多語句堆疊、data-modifying CTE、SELECT … INTO 與 MongoDB $out / $merge 一律拒絕,不會被讀成唯讀操作。Elasticsearch 以影響範圍判定:刪除單一文件是 data-admin,刪除整個 index 是 admin。

黑名單管理器 (Blacklist Manager)

從所有查詢結果中自動屏蔽敏感資料表與欄位,確保隱私安全。判定涵蓋語句參照到的每一張表,不只排在最前面那張:JOIN、逗號、UNION 或子查詢帶進來的黑名單表一樣會被擋下。Redis saved query 與 report 診斷會在執行前解析 key target、套用該連線的 blacklist 與 mask 規則,並從 SCAN evidence 移除受保護的 key 名稱。

查詢風險分析器 (Query Risk Analyzer)

透過 plan 進行靜態 SQL 風險分析,在連線任何線上資料庫前找出風險。

Antigravity 協議

將工作流拆分為 Architect(架構師/規劃)與 Builder(建設者/執行),確保行動前必有策略。

快速入門

使用您偏好的套件管理器進行全域安裝:

# 使用 Bun(執行時必要)
bun install -g @carllee1983/dbcli

# 使用 npm
npm install -g @carllee1983/dbcli

dbcli 執行於 Bun 1.4.0+。npm 與 npx 可作為發布通道,但安裝後的 dbcli 執行檔需要 PATH 上有 Bun;只有 ./agent-core 這個 subpath export 能被純 Node 程序 import。

單獨執行 dbcli --version 或 dbcli -V 時會使用輕量 launcher,不載入資料庫 driver;其他指令仍會照常載入完整 command runtime。

透過互動式導覽初始化您的第一個連線:

dbcli init

若是 MongoDB,init 會逐欄詢問連線資訊(Host、是否為 SRV、Port、User、Password、authSource,最後是可選的 replicaSet / tls)——完整欄位列表與 --uri 的拆解規則請見下方「引擎支援矩陣」一節的「MongoDB 連線設定」。

每次新建或新增連線都必須有非空白的連線名稱。可傳入 --conn-name <name>,或在互動式終端機執行 dbcli init,由程式主動詢問。名稱提示不會建議 default;輸入空字串或只有空白時,會說明名稱必填並再次詢問。取消或結束名稱提示會在連線測試、設定或憑證寫入前停止。明確提供非空白的 --conn-name 就不會再問名稱,--conn-name default 仍然合法。明確傳入空白的 --conn-name 則在任何模式都會於連線測試或寫入前報錯。--name 仍代表資料庫名稱(Redis 則是邏輯資料庫索引),不能當作連線名稱。

沒有 TTY、指定 --no-interactive,或 --use-env-refs 搭配全部五個 --env-* 選項而自動停用互動時,缺少或空白的 --conn-name 會在存取資料庫、寫入設定或憑證前明確報錯,並提示補上名稱。完整憑證、--name、--env-file 與 --force 都不能代替連線名稱。

新初始化一律建立具名 v2 設定,包含單純的 init 與明確的 --config 路徑,不再建立未命名的 v1 設定。既有 v1 設定仍可讀取,也保留原有的具名 v2 匯入流程。全新 registry 的第一條明確具名連線仍會成為預設;新增到既有 registry 時,則保留原本的預設值或無預設狀態。明確選擇已存在的名稱時,仍須互動確認覆寫或指定 --force;非互動同名操作未帶 --force,或拒絕互動確認時,都不會改動檔案。--remove 與 --rename 不會詢問新的連線名稱。

在幕後,init 會在專案的 ./.dbcli/config.json 寫入一個 version: 3 的 binding stub,真正的連線設定與任何憑證則存放在家目錄的 ~/.config/dbcli/projects/<project-name>-<sha1-12>/。如此可還原的敏感資料不會留在專案工作區,掃描 repo 的工具或 AI agent 也看不到。專案的 .dbcli/ 只保留 binding 與非敏感快取(schema 快取、稽核記錄、快照、驗證產物)。

若要讓多個專案共用連線,請使用明確的全域 scope。它會把 v2 registry 儲存在 ~/.config/dbcli/config.json,且不會建立專案 binding:

dbcli --global init --conn-name shared --system postgresql \
  --host db.example.com --port 5432 --user app --password '<secret>' \
  --name appdb --skip-test --no-interactive --force
dbcli --global use --list --format json
dbcli --global status --format json

Root 層級的 --global 必須放在指令之前。全域 registry 與專案 registry 彼此獨立;未帶 --global 時仍使用目前專案的 registry。全域檔案與 home storage 的專案設定一樣,會使用 integrity record 與私有檔案權限保護。

遮蔽輸入憑證

互動式 init 以遮蔽提示收集資料庫密碼,以及貼上的 MongoDB 連線字串(它本身就帶著憑證),輸入值不會留在終端機捲動紀錄、錄影或螢幕分享畫面裡。Host、Port、使用者名稱、資料庫名稱與環境變數名稱維持一般的可見提示。

沒有純文字的退路。當遮蔽輸入無法提供時——沒有終端機,或提示實作載入不到——init 會在寫出任何設定之前停下來,並指出你可以改用哪一種輸入:--password、從 .env 或行程環境變數解析到的憑證、--use-env-refs,MongoDB 則是 --uri。明確給定的 --password 或 --uri 會原樣保留,也不會被當成可見的提示預設值印出來;--no-interactive 則根本不會走到任何 secret 提示。

若接下來的連線測試失敗,driver 的訊息與提示會先遮蔽並截斷再輸出——driver 經常把憑證或整條連線 URI 原樣寫回錯誤訊息裡。

這解決的是終端機回顯,不是儲存:dbcli 不會加密靜態憑證。要讓憑證不落在設定檔裡,用的是 --use-env-refs 與家目錄儲存。

PostgreSQL、MySQL 與 MariaDB 驗證式 TLS

建立具名 PostgreSQL、MySQL 或 MariaDB profile 時,必須一起提供兩個 SQL TLS 參數。以下範例會詢問密碼,並在保存前測試連線:

dbcli init --conn-name verified-pg --system postgresql \
  --host db.example.com --port 5432 --user reader --name appdb \
  --permission query-only --sql-tls verify-full --sql-tls-ca ./certs/root-ca.pem
dbcli --use verified-pg blacklist list
dbcli --use verified-pg schema public.events --format json
dbcli --use verified-pg query "SELECT id FROM public.events LIMIT 10" --format json

MySQL 與 MariaDB 使用相同參數,各自保存具名 profile 與 CA 路徑。選取對應 profile 後,同樣先檢查 blacklist 與 schema,再執行已獲授權的查詢:

dbcli init --conn-name verified-mysql --system mysql \
  --host mysql.example.com --port 3306 --user reader --name appdb \
  --permission query-only --sql-tls verify-full --sql-tls-ca ./certs/mysql-ca.pem
dbcli init --conn-name verified-mariadb --system mariadb \
  --host mariadb.example.com --port 3306 --user reader --name appdb \
  --permission query-only --sql-tls verify-full --sql-tls-ca ./certs/mariadb-ca.pem

請將主機、資料庫與資料表換成已獲授權的目標,並先透過 schema 確認實際欄位再查詢。保存的連線會包含獨立的 SQL 設定物件(以下為連線片段):

{
  "sqlTls": {
    "mode": "verify-full",
    "caPath": "/absolute/path/to/certs/root-ca.pem"
  }
}

--sql-tls verify-full 與 --sql-tls-ca <path> 必須成對提供;空值、其他模式與不支援的引擎都會明確失敗。此 SQL TLS 契約支援 PostgreSQL、MySQL 與 MariaDB;其他引擎會拒絕明確要求。MongoDB 的 tls 與 Elasticsearch 的 TLS 設定仍是各自獨立的契約。未設定 sqlTls 的既有 profile 維持原行為,不新增全域 TLS 政策。V1 目錄與 v2 profile 的讀取、正常 init 從 V1 目錄匯入 v2,以及公開的 migrateV1ToV2 helper,均保留 TLS 物件。Legacy V1 單檔 TLS profile 仍可在人類模式下讀取與使用,但自動 init 匯入會在連線或寫入前明確拒絕,保留原檔不變。此情況請在獨立設定目錄建立具名驗證式 TLS profile,完成驗證後再選取;遷移全程保留 sqlTls。

init 會以執行時的工作目錄解析相對 CA 路徑,再保存為絕對路徑。保存的 caPath 必須是非空白的 PEM CA bundle 絕對字面路徑,不接受環境變數參照;host/user 等既有欄位的受支援環境參照仍可使用。明確 TLS 只使用所選 profile 的 CA 與身分設定;ambient TLS 設定不能降低驗證要求或帶入其他 profile 的信任條件。每次建立連線都會重新讀取 CA bundle;無法讀取、空白或無效的 bundle 會在連線前停止。--skip-test 只略過初始化測試,不會略過後續使用時的驗證。

verify-full 要求加密、可信憑證鏈、有效日期,以及憑證身分符合設定的 DNS 主機名或 IP 位址。明確 SQL TLS 會拒絕 Unix socket。不受信任、過期或主機名不符的憑證,以及不支援 TLS 的伺服器,均在使用者查詢前失敗;不會明文重試或降低驗證要求。請修正 CA、目標主機名或憑證有效期後重試。只有 cipher 證據不能證明身分驗證成立。

TLS 錯誤復原時,請保留 TLS 設定,先修正 CA、主機名或憑證,再以 --config <path>(或 --global)明確選取設定;v2 還需以 --use <name> 選取具名 profile(也接受 DBCLI_CONNECTION)。所選 profile 必須仍有 sqlTls.mode: verify-full;缺少、無法明確解析或明文連線選取會阻擋即時復原步驟,不會退回預設明文連線。保存的 recovery envelope 已遮蔽原始 selector;目前選取代表你刻意指定的目標,不是原始目標的證明。Recovery apply 與驗證使用目前執行的 CLI,避免呼叫 PATH 上的舊版 dbcli。以上述 MySQL 範例為例:

bun run src/cli.ts --config .dbcli --use verified-mysql recover --apply --format json

Agent mode 仍允許透過已設定 profile 執行權限內的唯讀操作;DBCLI_AGENT_MODE=1 仍拒絕 init 與其他設定修改。TLS 不會繞過 production 明確選取、原生資料庫授權、permission、黑名單、write gate 或 audit 控制。

回滾執行檔時,請停止需要驗證式 TLS 的流程,或維持支援此功能的版本。舊版可能剝除 sqlTls;降版後保留 profile 並不保證仍符合要求。移除 TLS 設定或改用明文重試都不能恢復符合。本機隔離 PostgreSQL、MySQL 與 MariaDB fixture 結果不代表真實 RDS 已通過驗收;RDS 仍未驗證。

連線管理

在正式 (Prod)、測試 (Staging) 及本地 (Local) 環境間切換:

使用 --global 可在不依賴目前專案的情況下管理或執行 user-level registry 的具名連線:

dbcli --global use --list
dbcli --global use shared
dbcli --global query "SELECT 1"

未帶 --global 時,dbcli 仍會解析目前專案的 .dbcli binding。明確指定 scope 可避免在不相關的專案中意外選到全域連線。

Root-level --timeout <ms> 可針對單次執行覆寫連線 timeout(整數,100~600000;必須放在指令之前,與 --global、--use 同樣是 root-level flag)。它會覆寫連線設定中的 timeout 欄位;兩者都沒設定時,各 adapter 沿用內建的 5000ms 預設值。這個覆寫只在本次建立連線時套用,不會寫回 config.json;要永久生效請在連線設定裡寫 timeout 欄位。Elasticsearch 是把 timeout 套用在每個 request 上,而不是整條連線一次性生效。當預設值太緊時很實用,例如 MongoDB 跨 VPN 或連 Atlas:

dbcli --timeout 20000 --use <conn> list

連線逾時與語句逾時是兩個獨立的上限。--timeout 會同時收緊兩者,所以 --timeout 3000 也會中止跑超過 3 秒的 SQL。只想調整「一句查詢能跑多久」時,用 root-level --statement-timeout <ms>(整數,0~3600000;0 表示取消上限)或連線設定的 statementTimeout 欄位。兩者都沒設定時 dbcli 不設語句上限,交給伺服器自己的設定——跑六秒的分析查詢不會再因為連線逾時預設五秒而被砍掉:

dbcli --timeout 2000 --statement-timeout 120000 query "SELECT ... FROM big_table"
dbcli use --list         # 列出所有連線名稱
dbcli use --list --format json # 不含憑證的 JSON 連線清單
dbcli use --unset        # 清除預設值,不移除連線
dbcli use staging        # 切換預設連線
dbcli --use prod query "SELECT count(*) FROM users" # 全域單次 selector
dbcli query --use prod "SELECT count(*) FROM users" # 支援的指令層級寫法
DBCLI_CONNECTION=prod dbcli query "SELECT count(*) FROM users" # 環境變數 selector

若要提供明確且不含機密的環境標籤,可在 v2 具名連線設定可選的 environment 欄位,例如 "environment": "production"。dbcli use --list --format json 會回傳每個連線的名稱、環境標籤(或 null)、權限、系統、伺服器/資料庫識別與預設標記;由環境變數提供的識別欄位會是 null,只有 URI 的 MongoDB 與只有 Cloud ID 的 Elasticsearch 也會以 null 取代預設占位值。它刻意不輸出 user、password、URI、Cloud ID、API key 或環境變數名稱。拼錯連線 selector 時,dbcli 會提示相近的既有名稱。

在 v2 中,實際選取的連線名稱會保留到指令執行期間,因此 audit 記錄會路由到該連線自己的 audit stream。

正式環境與自動化防護

標示為 environment: "production" 的連線,若要設成持久化預設值會預設拒絕;必須重複輸入完全相同的名稱明確確認:

dbcli use production --confirm-production production

此確認只適用於持久化預設值的變更:單次 --use production 仍是明確選取,且不會修改設定。在 agent mode(DBCLI_AGENT_MODE=1)中,設定、權限與憑證變更一律被拒絕。人類或管理員必須在關閉 agent mode 的獨立 process 執行核准流程;dbcli 不接受同一個 process 的環境變數作為核准。受信任的寫入會維護 config integrity record,並在支援時設定安全檔案權限;agent 讀取遇到缺失、替換、非一般檔案或竄改的 record 時會 fail closed。agent mode 也會拒絕 legacy 單檔 .dbcli 設定,必須先由人類/管理員流程遷移到 V2 home storage。若要防護同一 OS 使用者的惡意 process,請將 DBCLI_CONFIG_INTEGRITY_ANCHOR_DIR 設為由 host 保護或唯讀掛載的目錄,讓 detached digest 不會和工作區檔案一起被替換。

每筆 audit entry 都會從解析後的連線寫入非機密的 metadata.connection_name 與 metadata.environment,絕不記錄憑證或 endpoint secret。SQL 的紀錄另外帶 metadata.blacklist_checked——黑名單拿這句語句去比對過的每一個識別子。target 只有一個表名、由另一套推導產生,遇到 JOIN 取的是排在前面那張:一句因為碰到 salaries 而被拒絕的語句,紀錄可能填 target: "a",於是用 target 去問「有沒有人碰受保護的表」會查不到。它原樣保存黑名單看到的東西,所以刻意不只有表名——別名、含點的欄位參照、以及 CREATE 這類關鍵字都在裡面,因為為了好看去過濾,等於再造一套與原本那套對不起來的解析。

選擇優先序為明確的 --use、DBCLI_CONNECTION、最後才是設定檔預設連線。環境變數值會移除前後空白,空值則視為未設定。單次 selector 不會修改持久化預設值。query、schema、list、export、check 支援指令層級寫法;其他指令請把全域 selector 放在指令之前。root 與指令層級值衝突時會清楚失敗。Selector 需要 v2 設定:單連線(v1)專案沒有具名連線可選,因此在 v1 下給 --use 或 DBCLI_CONNECTION 會被拒絕,而不是靜默改跑那唯一的連線。若把 --use 放在不支援的指令後面,dbcli 會維持拒絕並印出可直接複製的 root-level 寫法,例如 dbcli --use <connection> status。

不設定預設連線

v2 設定可以省略最外層的 default 欄位。如果提供此欄位,必須是指向既有連線的非空字串;null、"" 與不存在的名稱都無效。新 registry 的第一條明確具名連線會成為預設;新增連線則保留原本的預設值或無預設狀態。既有 v1 讀取與具名 v1 升級到 v2 的匯入流程仍受支援。

dbcli use --unset
dbcli --use staging query "SELECT 1"
DBCLI_CONNECTION=staging dbcli query "SELECT 1"

沒有儲存的預設連線時,需要連線的指令必須透過 --use 或 DBCLI_CONNECTION 明確選取,否則會在存取資料庫之前失敗,並列出可用的連線名稱,不包含端點或憑證。 dbcli 不會自動挑選第一條連線。上述選擇優先序與衝突檢查仍適用:CLI selector 不得空白,空白的環境變數 selector 則視為未設定。不存在的名稱與正式環境連線仍受既有的驗證及明確選取防護約束。

dbcli use --unset 只移除 v2 設定中持久化的 default 欄位,並回報已清除。欄位已不存在時也會成功,且不會修改連線。若同時提供連線名稱、--list、 --confirm-production 或 --format json,會拒絕且不寫入;它也不會轉換或覆寫 v1 設定。清除預設值與切換預設值使用相同的核准及 integrity record 保護寫入流程,在 agent mode 中仍會被擋下。

dbcli use 會說明目前沒有預設連線;文字清單不會將任何連線標為預設,JSON 清單的每筆連線都會是 isDefault: false,doctor 也會將省略預設值視為有效的 v2 設定;其連線檢查仍須明確指定 selector,例如 dbcli --use staging doctor。 dbcli init --remove <name> 若移除的是預設連線,會清除預設值並回報此變更,不會另選替代連線。移除其他連線時,會保留原本的預設值或無預設狀態;刪除最後一條連線則會被拒絕,設定不會改變。連線更名會保留無預設狀態;若更名的是目前預設連線,則會同步更新預設名稱。要重新設定預設值,請使用 dbcli use <name>;正式環境的確認要求維持不變。

舊版專案設定相容性:如果新增連線會匯入直接存放在專案內的 v2 registry,且既有連線的 envFile 不是 .env.local,init 會在寫入前拒絕,保留原設定與 env 檔案。請先由管理員將 registry 與自訂 env 檔案遷移到受支援的 home storage,再重試。一般已綁定/home-storage v2 設定,以及未設定 envFile 或只使用 .env.local 的直接專案 v2 設定,仍可正常使用。

伺服器強制的 SQL query-only 模式

在 PostgreSQL、MySQL 與 MariaDB 上,只要操作的有效權限是 query-only,每一句由呼叫端提供的 SQL 都會在實際執行它的 physical connection 上,包進一個全新的資料庫原生唯讀 transaction。這涵蓋 query、export、saved query 本體與驗證、report diagnostics、SQL shell、會實際執行的 explain plan,以及所選連線即使儲存較高 permission tier 的 fan-out。dbcli 若無法建立此邊界,會在送出目標語句前 fail closed。既有的 classifier、blacklist、hidden-write 與 multi-statement 檢查仍會先執行。若目標語句完成後 transaction 清理失敗,dbcli 會回報結果不確定並丟棄受影響的連線;SQL shell 會為後續語句重新連線,但不會重送已完成的目標語句。

這項保證涵蓋持久、非 temporary 的資料與 schema。各引擎的原生唯讀規則仍可能允許 temporary 或 session-local state;此邊界也無法阻止目標資料庫之外的副作用,例如不安全 extension 發出的網路呼叫。資料庫帳號與 ACL 仍是重要的獨立防線。

唯讀 query fan-out

明確指定逗號分隔的 --use,即可對多個具名連線執行同一個唯讀 query。Root 層級與指令層級寫法效果相同:

dbcli --use primary,staging query "SELECT count(*) FROM users" --format json
dbcli query --use primary,staging "SELECT count(*) FROM users"

連線名稱會移除前後空白,結果則維持 selector 的順序;空白或重複名稱會被拒絕。只指定一個名稱時仍走既有的單連線路徑。Fan-out 只會由明確的 --use 啟用:DBCLI_CONNECTION 仍代表一個完整的連線名稱,不會依逗號拆分。兩種 --use 寫法都不會修改持久化的預設連線。

在任何 adapter 建立連線前,dbcli 會先載入並驗證所有選定的連線配置。SQL fan-out 只允許唯讀分類(SELECT、SHOW、DESCRIBE、EXPLAIN);MongoDB 允許 filter object 與唯讀 aggregation pipeline,但拒絕頂層 $out 或 $merge stage;Elasticsearch 只允許 search。Redis 不支援 fan-out。多連線 query 也會拒絕 --recovery、--ui、CSV 與 HTML 輸出;請使用 --format table(預設)或 --format json。

每個連線都會獨立使用自己的 adapter、blacklist 過濾、row-limit/截斷 metadata、audit entry、計時與 disconnect;其中一個連線失敗不會取消或隱藏其他結果。JSON 會依序回傳 results array,每個連線都有標示清楚的 ok 或 error outcome;table 則依各自 schema 顯示獨立且具連線標籤的區段。全部成功時 aggregate exit code 為 0,成功與失敗混合時為 2,全部失敗或執行前拒絕請求時為 1。

密碼輪替

dbcli password 只變更單一連線的密碼,其他設定完全不動——專為密碼會定期自動輪替的環境設計。

dbcli password                       # 遮蔽輸入,輪替預設連線
dbcli password prod                  # 遮蔽輸入,輪替 'prod'
rotate-secret | dbcli password prod --stdin   # 非互動,密碼不會留在 shell 歷史
dbcli password prod --password "$NEW" --skip-test --format json

密碼寫到哪裡是從設定檔讀出來的,不是用猜的:若連線的 password 是 { "$env": "NAME" },就會改寫該連線 envFile 裡的 NAME。連線沒宣告 envFile 時,輪替會順便把 envFile(.env.local)記錄進該連線設定——不記錄的話讀取端根本不會載入那個檔案。若連線目前仍是明文密碼,會一次性轉換成 { "$env": "DBCLI_<CONN>_PASSWORD" },之後的輪替就只會動 env 檔。值會以加引號的形式寫入(NAME="..."),密碼首尾的空白因此能原樣保留。

v1 設定則會改寫 .env.local 裡的 DBCLI_PASSWORD,與 v1 的讀取邏輯一致。若 v1 設定的密碼來自其他環境變數(不是 DBCLI_PASSWORD),指令會直接報錯說明:v1 沒有 per-connection env 檔,dbcli 寫任何檔案都無法讓那個變數有值,請直接更新環境變數,或升級到 v2。

預設會先用新密碼實際連線驗證,通過才會寫入檔案,避免輪替失敗卻留下壞掉的憑證。資料庫連不到時可用 --skip-test 跳過驗證。env 檔在 POSIX 系統上會以 0600 權限寫入(Windows 沒有對應的權限位元,檔案沿用所在目錄的 ACL),密碼不會被回顯或寫進 log。

選項: [connection]、--stdin、--password <value>(會留在 shell 歷史與行程列表中,建議優先用 --stdin)、--skip-test、--format <text|json>。

在 DBCLI_AGENT_MODE=1 下,和其他憑證變更一樣會被擋下。

指令詳解

探索與發現

指令用途
list列出資料表、集合、鍵或索引。
schema顯示結構並重新整理本地元資料快取。
inspect上下文快照(物件、權限、建議行動)。
status配置與連線健康度摘要。
capabilities靜態 capability catalog(dbcli 能做什麼)。不連線資料庫。
capabilities check依本機設定檢查所需的 capability id。不建立連線。

dbcli blacklist list --format json 會在 stdout 輸出一個 machine-readable 文件,包含 tables、columns 與 warnings;不合法的 MongoDB blacklist path 會放進結構化的 warnings 陣列,不會把人類可讀診斷混入 stdout。

檢視單一 SQL 物件的完整名稱

PostgreSQL 使用 schema.table,MySQL 與 MariaDB 使用 database.table,可檢視一個有權存取的表或 view(包含系統視圖)。欄位、enum、鍵、索引與外鍵均綁定同一 namespace 與局部名稱。不會變更連線、PostgreSQL search_path、預設 database 或 schema 快取;list、掃描與不帶物件的 schema 範圍不變。

dbcli schema pg_catalog.pg_roles --format json
dbcli schema information_schema.TABLES --format json
dbcli schema '"Audit"."Order.Items"' --format json
dbcli schema '`reporting`.`order.items`' --format json

以上 shell 範例外層的單引號會保留引擎原生識別字引號:PostgreSQL 用雙引號,MySQL/MariaDB 用反引號。每個 component 分別引用,以保留大小寫或名稱內的點號;內嵌的原生引號以連續兩個引號跳脫,例如 PostgreSQL 的 "Audit"."Order""Items"。PostgreSQL 完整名稱中未引用的 component 會折成小寫;MySQL/MariaDB 依伺服器的識別字大小寫規則處理。空 component、超過兩層、無效或未閉合的引號均拒絕,不會猜測另一種解讀。

未限定名稱保留既有精確名稱語意:PostgreSQL 使用 public,MySQL/MariaDB 使用連線的預設 database。完整名稱不存在時不會退回預設 namespace 的同名物件。相容性提醒:局部名稱本身含點號時,現在必須明確指定,例如 dbcli schema 'public."audit.orders"' --format json。此名稱解析僅適用於單一物件的 schema,不改變其他命令。

完整名稱的 JSON 輸出保留局部 name,另提供獨立的 schema 字串(MySQL/MariaDB 為 database 名稱);文字輸出也會區分兩個 component,包含引用的點號與大小寫敏感名稱。tableType 為 table 或 view;PostgreSQL materialized view 歸為 view。完整名稱查詢不對關係資料執行 COUNT(*)。可用的 metadata 估計值只是估計,不是精確筆數;未知時 JSON 為 estimatedRowCount: null 與 sizeCategory: null,不虛構 metadata.rowCount: 0。文字不會以零筆或 small 分類替代未知值。既有未限定名稱的計數行為不變。

SQL 單一物件查詢的未限定與完整名稱都套用 blacklist。bare 或 qualified 規則任一命中即生效,沿用既有 glob 與大小寫匹配。受保護的表會拒絕查詢;受保護欄位會隱藏,含該欄位的主鍵或索引也會整項省略。外鍵來源或目標的表/欄位受保護時,表層級與欄位層級的外鍵 metadata 都會整條省略,複合約束不會被裁切成誤導的部分約束。此單一物件 schema 命令也會在無法安全表達完整欄位相依性時,整項省略 expression 或 partial index。省略 metadata 時,JSON 會輸出 metadataOmitted: true,文字輸出會顯示省略通知,可與原本不存在的 metadata 區分;回傳的 metadata 不一定完整。全欄位被隱藏不代表物件不存在。必要 metadata 讀取失敗(包含原生權限不足)會使命令失敗,不會以成功的空集合代替;資料庫無法區分不存在與不可見時,錯誤會保留此不確定性。

完整名稱不可與 --refresh 或 --reset 並用(也不能同時帶兩者),命令會在掃描前拒絕。快取維護請另行使用不帶完整名稱的 schema --refresh 或 schema --reset,範圍沿用既有行為。完整名稱檢視不會提升原生權限,也不會繞過 query-only、agent mode 或 blacklist 保護。

給 Skill 的能力探索

dbcli capabilities 用一個有版本、可解析的形狀回答「這個工具能做什麼」,讓外部 Skill 在動工之前就能決定。dbcli capabilities check --require <ids> 接著依本機設定回答「這裡有沒有」。兩者都不會建立資料庫連線。

dbcli capabilities --format json
dbcli capabilities check --require schema.read,query.read --format json

connection.tls 由引擎 capability matrix 表示 PostgreSQL、MySQL 與 MariaDB 的支援能力,對應既有連線指令介面,不新增 TLS 子指令。可執行 dbcli --use verified-pg capabilities check --require connection.tls --format json。available 只表示工具支援,不證明此 profile 已設定 TLS、CA 可讀取、握手成功或操作已獲授權。Catalog/check 維持離線,不讀取 CA bundle、不握手、不修改設定。其他引擎不會回報此能力為 available;沒有此 ID 的舊版回傳 unknown。設定修改仍需獨立授權。

套件內的 assets/integration-kit/ 提供可直接複製的 Bun/TypeScript consumer 與 Task Pack fixture。它會 pin 兩個 schema version、strictly parse catalog 與 Operation Envelope,並示範 exit、correlation 與 evidence 的邊界。其中的 CRUD、CQRS 與 DBA 範例仍是外部 consumer 的需求;dbcli 不實作也不核准這些工作流程。

Agent output v1

--agent-output 是 root option,必須放在 subcommand 前。PLAT-005 支援 dbcli --agent-output capabilities 與 dbcli --agent-output capabilities check --require <ids>;兩者都會在 stdout 輸出一份 compact UTF-8 JSON 文件與一個換行,stderr 不會有任何輸出。它是暫態的:不是 evidence,也不會建立 timestamp。

可在 subcommand 前使用可選 root option --correlation-id <id>,把一次 invocation 關聯到 Story、incident、change request、migration 或 backfill。ID 限 1–160 個 ASCII 英數字、點、底線、冒號或連字號;不可放憑證、SQL、個人資料或自由文字標籤。已支援且非 static 的回應會在 context.correlationId 顯示 ID,既有 audit entry 會在 metadata.correlation_id 記錄它;command summary 會遮蔽它。Static capabilities 仍保持 context: null。此 option 不會建立 evidence,也不會改動 receipt。

v1 envelope 一律有十個 key:schemaVersion、ok、operation、status、context、data、warnings、evidence、recovery 與 error。schemaVersion 是 1;operation 是 capabilities.list 或 capabilities.check;status 是 succeeded 或 failed;consumer 必須依 key 名稱讀取,不能依欄位順序。文件連同換行上限為 64 KiB。

不可把 --agent-output 和明確提供的 --format 或 --for-agent 一起使用;這類衝突、放在 subcommand 後、無效輸入或不支援的 operation,都會回傳一份 envelope 並以 exit 2 結束。成功 exit 0;需求未滿足或未預期的 internal failure exit 1。未使用 --agent-output 時,既有輸出行為不變。

error 與 warning 的 code/message vocabulary 永遠是 locale-independent English。Error code 為 INVALID_AGENT_OUTPUT_OPTIONS(無效、位置錯誤或衝突的 output option)、UNSUPPORTED_AGENT_OUTPUT_OPERATION(PLAT-005 範圍外的 operation)、INVALID_CAPABILITY_REQUIREMENTS(無效的 --require)、CAPABILITY_REQUIREMENTS_UNMET(已完成的 negative capability result)、AGENT_OUTPUT_LIMIT_EXCEEDED(64 KiB 上限)、AGENT_OUTPUT_INTERNAL_ERROR(未預期但安全的 failure)與 INVALID_CORRELATION_ID(無效的 root correlation ID)。Warning code 為 DUPLICATE_CAPABILITY_REQUIREMENT、CAPABILITY_CONTEXT_UNAVAILABLE、CAPABILITY_CONTEXT_UNRESOLVABLE 與 AGENT_MODE_RESTRICTION_ACTIVE。

一個 capability 是 dbcli 的單一原子能力——schema.read、data.delete。它絕不是職務或方法:dba.tune-production 與 crud.scaffold 屬於組合 dbcli 的 Role Skill 或 Method Skill。分層是 Story + AGENTS.md + Role Skill + Method Skill + Tool Skill + dbcli,而 dbcli 只回答最後兩層。

輸出刻意不代表的四件事:

狀態有 available、unavailable 與 unknown。unavailable 的原因有 engine、agent-mode、permission、context-unavailable 與 context-unresolvable,依「最難修的排前面」回報,讓 reason 指到真正擋路的那一個。在 DBCLI_AGENT_MODE=1 之下,會變更設定的能力一律被拒,不管權限等級寫什麼。context-unresolvable 與 context-unavailable 刻意分開:{"$env": "..."} 密碼指向未設定變數的設定檔是存在且可讀的,宣稱「沒有設定」是假話。

無法辨識的 id 一律 fail closed,絕不會被解析成長得像的那一個。Exit code:0 全部可用、1 有任一不可用或未知、2 輸入無效。

Catalog 涵蓋所有公開指令,包含 capabilities 自己,而且每一條 engine 宣稱都是從實作讀出來的。程式碼會直接拒絕非 SQL 連線的那些指令——explain、plan、assert、snapshot、verify、proxy——在 MongoDB、Redis 與 Elasticsearch 上會回 unavailable 加上 reason engine。只有某一個模式需要 SQL 的指令——impact assess 與 design 的 --against-cache、semantic 的 draft validate——在那三個引擎上仍是 available,只標成 limited。程式碼判定不了的引擎,一律不會被標成支援。

required 與 results 依首次出現的輸入順序(first-seen input order)保留你送進來的 id,因此 results[i] 回答的就是 required[i],重複的 id 只出現一次。把參數換個順序等於換了一份輸入,輸出也會跟著換順序——相同輸入才保證 byte-identical。換順序不會改變的是任何 capability 的 status、reason 與整體 ok。

inspect 給 agent 的輸出

dbcli inspect 回傳兩個平行陣列,讓 agent 在第一次呼叫就能定位:

兩個陣列在 --for-agent / --brief 下都會被裁切(hints ≤ 3 條,suggestedCommands 只留最安全的 1 條)。

查詢與資料操作

# 原生查詢
dbcli query "SELECT * FROM users LIMIT 10"

# 靜態 SQL 顧問 (不連線資料庫)
dbcli lint "SELECT * FROM users" --format json

# 執行 Snippet
dbcli q @snippet --param id=1

# 匯出資料
dbcli export "SELECT * FROM logs" --format csv --output logs.csv

# 安全寫入
dbcli insert users --data '{"name":"Alice"}' --dry-run

# DML --plan 預檢 (不連線資料庫;SQL、MongoDB、Redis、Elasticsearch)
dbcli insert users --data '{"name":"Alice","email":"a@b.com"}' --plan --format json
dbcli update users --where '{"_id":"abc"}' --set '{"status":"inactive"}' --plan
dbcli delete products --where '{"_id":"abc"}' --plan --format json
dbcli delete 'user:42' --where '' --plan --format json

寫入確認閘門(2.0.0)

所有寫入都會經過兩級閘門。第一級是一個問題,第二級是一道沒有旗標可以繞過的拒絕。

第一級——一般寫入。 INSERT、帶 WHERE 的 UPDATE / DELETE、CREATE、ALTER。在互動終端下,dbcli 會先印出它把這句理解成什麼,再問你要不要執行;--yes 可以跳過。非互動執行(stdout 被導向、CI、agent)根本不會看到這個問題,--format json 即使在終端下也會關掉它。

第二級——收不回來的寫入。 沒有 WHERE 的 UPDATE / DELETE、DROP、TRUNCATE、SQL parser 讀不懂的語句、一個字串裡塞了多句語句、裡面夾著另一個寫入的語句(資料修改型 CTE,例如 WITH x AS (DELETE FROM t RETURNING *) INSERT INTO …)、帶 WHEN … THEN DELETE 或 THEN UPDATE 動作的 MERGE,以及 dbcli update / dbcli delete 中 --where 沒有命中主鍵或唯一索引的情況。dbcli 會要求你打出目標資料表的名稱,沒有任何旗標可以跳過,--yes 與 --force 都不行。當現場沒有人時,dbcli 直接拒絕:退出碼 1、什麼都不會送到資料庫,訊息會點名 reason=no_where、reason=ddl_destruction、reason=unparseable、reason=multiple_statements 、reason=non_unique_where、reason=multi_table 或 reason=nested_write。

接了另一張表的寫入一律第二級,不管 WHERE 寫什麼。 這種寫入會動到幾列取決於資料而不是語句:DELETE p FROM p JOIN o ON p.id = o.ref WHERE o.x > 0 在一組資料上刪了 5 列中的 2 列,在另一組上刪光 2000 列。join 的 ON 一定會提到目標表,所以它什麼也證明不了。這會讓 UPDATE … FROM、UPDATE … JOIN … SET 與多表 DELETE 這些標準寫法對無人看管的呼叫端被拒——把另一張表改寫成 WHERE 裡的子查詢,或改在有人可以確認的地方執行。

單表寫入的 WHERE 必須是在講那張表。 只存在於子查詢裡的 WHERE 不算:UPDATE p SET c = (SELECT max(x) FROM o WHERE o.id = 1) 有 WHERE,但改寫每一列。回頭指向目標表的相關參照則算數,所以 DELETE FROM t WHERE EXISTS (SELECT 1 FROM o WHERE o.tid = t.id) 是普通寫入,同一句改成裡面寫 WHERE o.id = 1 就會刪光整張表,屬第二級。這是下界而不是保證——WHERE id IS NOT NULL 提到了目標表,卻依然動到每一列。

逃生路徑寫在語句裡,不是寫在旗標上。 要在無人看管下執行全表寫入,就把意圖寫進 SQL——補上 WHERE 1=1 或 LIMIT。這是刻意的設計:對已經有 WHERE 的語句再接一個 WHERE 1=1 是語法錯誤,所以「乾脆全部都加上去」會立刻壞掉,養不成習慣;旗標的性質剛好相反。DROP 與 TRUNCATE 沒有子句可加:能不能執行由連線的 permission 層級決定,而打字確認仍然必須由人完成。

dbcli shell 只接第二級,不接第一級。shell 裡的每一句都是人手打的,逐句 y/N 會變成反射動作;但沒有 WHERE 的 DELETE、DROP、TRUNCATE 一樣要打出資料表名稱。打錯只會印出「已取消」並回到提示符——連線、緩衝區與歷史都還在,行程不會結束。按 Ctrl-C 則是收回這個提問:什麼都不會執行,接下來打的那一行會被當成語句,而不是被吃成確認答案。以管線餵進來的輸入(dbcli shell < script.sql)沒有人能回答,該句會被拒絕,其餘的行照跑。Redis、MongoDB 與 Elasticsearch 的 shell 不走寫入閘門,改由連線的 permission 等級把關。Elasticsearch 這一道在 4.0.0 之前並不存在 —— ES shell 完全沒有 permission 檢查就送到叢集,query-only 連線可以刪除文件、刪掉索引或改寫 mapping,而且什麼都沒被記錄。現在它用與 dbcli query 相同的分類器判斷每個請求,拒絕該等級不允許的操作,並且不論執行或被拒都寫入 audit —— 送出前一列、回應後一列,每列都以 <METHOD> <path> 寫明操作本身。伺服器端 script(script、script_fields)在 shell 裡也一律拒絕,與 dbcli query 一致:它們會在叢集上執行任意程式碼,並能把黑名單欄位以請求自選的名稱讀回來。以管線驅動的 shell 現在會把 audit 寫完才結束,printf 'DELETE /orders\n\n' | dbcli shell 不再能送到叢集卻不留紀錄;SQL shell 的同一個缺陷一併修正。SQL shell 從 5.0.0 起為每一句語句寫稽核列,形狀與 Elasticsearch shell 相同——送出前 attempt、回來後 outcome,被拒絕的語句只寫 outcome,因為它從未被嘗試。先前它只記錄 tier-two 的寫入閘門決策,所以在提示符打的 SELECT 與一句真的改了資料的 UPDATE 同樣不留痕跡,而同樣這兩句走 dbcli query 都會被記下。兩個 shell 的 phase 都放在 metadata.shell_phase。audit 預設是 best-effort——磁碟滿或 .dbcli/audit 不可寫時請求照樣送出,只留一行警告——若你認為「沒有紀錄的請求」比「被拒絕的請求」更糟,在設定裡開 audit.strict: true。它涵蓋 dbcli 在執行之前記錄操作的每一處:兩個 shell 送出前那一列,以及 SQL 寫入閘門的決定紀錄——現在也包含在 dbcli> 提示符打的 SELECT。事後才寫的紀錄不在範圍內——那時拒絕擋不回任何東西,只會把一次已完成的操作回報成失敗。dbcli audit health 看得到它現在是開是關。在 shell 裡以子指令形式打的 dbcli 指令(query "..."、\delete ...)跑在沒有 stdin 的獨立行程裡,沒有辦法提問,所以第二級語句會被拒絕,訊息會請你回到 dbcli> 提示符直接打。

名稱與 SQL 關鍵字相同的子指令需要 \ 前綴。 delete users --where id=1 會被當成 SQL——shell 就是拿來打 SQL 的,DELETE FROM users WHERE … 一定要能照打——所以它會等分號而不是執行子指令。要呼叫子指令請打 \delete users --where id=1。insert、update、explain 同理——與 SQL 關鍵字同名的子指令就這四個;前綴對每一個子指令都有效,所以要記的是一條規則而不是一張清單。語句還在累積時,.quit 與 .clear 依然有效、Ctrl-C 可取消——它們原本會被吃進緩衝區,讓 shell 看起來像當掉。

子指令跑在自己的行程裡且沒有 stdin,所以它原本的確認提問會讀到 EOF 而取消:在 shell 裡 \insert、\update、\delete 需要加 --force,或者直接在提示符打 SQL——那裡才答得了。第二級的確認不受影響:它會在子行程裡被拒絕,並請你回到提示符,因為表名只有在那裡打得出來。

每一次第二級判定都會寫進稽核紀錄——放行、取消、拒絕都記——所以這道閘門有沒有擋到東西是可以量測的,不是靠印象。dbcli audit write-gate 就是那個量測:第二級被觸及幾次、依哪一條判準,以及最後怎麼被回答。如果數字是零,該檢討的是判準,不是閘門。

# 拒絕:沒有 WHERE,也沒有人能確認
dbcli query "UPDATE users SET banned = 1" --format json
# → 退出碼 1、reason=no_where,什麼都沒送到資料庫

# 放行:意圖已經寫在語句裡
dbcli query "UPDATE users SET banned = 1 WHERE 1=1" --format json

# 一般寫入,無人看管或直接跳過提問
dbcli query "UPDATE users SET banned = 1 WHERE id = 3" --yes

# 條件欄位不唯一的結構化刪除:第二級
dbcli delete users --where "status=active"

從 positional、檔案或 stdin 讀取 query

dbcli query [sql] [-f, --query-file <path>] 必須且只能指定一個 query 來源:positional query 文字、--query-file 指定的 UTF-8 檔案,或以 --query-file - 從 stdin 讀取。未提供來源,或同時提供 positional 文字與 --query-file 都會報錯。dbcli 讀取來源後會移除一個開頭的 UTF-8 BOM 與前後空白;處理後若為空內容,則會拒絕執行。--query-file - 需要 piped input:當 stdin 是互動式終端時,dbcli 會立即拒絕,而不是無提示地空等輸入。

透過 stdin 傳入多行 SQL,就不需要把整段內容 escape 成單一 shell argument:

dbcli query --query-file - <<'SQL'
SELECT id, email
FROM users
WHERE status = 'active'
ORDER BY id;
SQL

MongoDB filter 與 aggregation pipeline 也使用相同的檔案/stdin 來源。如果 pipeline.json 包含 [{"$match":{"message":{"$regex":"user's event"}}}],下列兩種寫法都不必處理其中對 shell 不友善的 apostrophe:

dbcli query --collection raw_logs --query-file ./pipeline.json

dbcli query --collection raw_logs --query-file - <<'JSON'
[{"$match":{"message":{"$regex":"user's event"}}}]
JSON

被動慢查詢提示

query 與 q 會觀察已完成查詢所回傳的執行時間。預設門檻為 1000ms;跨過門檻時,table 輸出會附上 Performance hint,JSON 輸出則會在 metadata.performanceAdvisory 提供結構化資訊。快速查詢的輸出維持不變,因此正常查詢不會反覆收到建議。

# 查詢花費 250ms 以上時才提示後續檢視方式
dbcli query "SELECT * FROM events WHERE account_id = 42" --slow-ms 250

# 為這次 invocation 關閉被動提示
dbcli q @daily-active-users --slow-ms 0

此提示不會執行 EXPLAIN、讀取 schema,也不會發出第二個資料庫請求。在 PostgreSQL、MySQL、MariaDB 與 Redis 上,它建議不會修改資料的下一步:dbcli guide slow-query --format markdown;更深入的診斷應在檢閱該指引與相關 query 後才執行。MongoDB 與 Elasticsearch 沒有對應該目標的診斷 snippet,因此提示只陳述耗時,不會指向一個查不到東西的指令。

使用 --fields 投影欄位

--fields 可將 SQL 或 MongoDB query 結果縮減為需要的欄位。使用 inclusion list 保留欄位,輸出順序會依照指定順序:

dbcli query "SELECT * FROM events" --fields id,name,created_at
dbcli query '{}' --collection raw_logs --fields station_code,bet,win,created_at

在每個欄位前加上 - 即可排除欄位。為了讓開頭的 hyphen 明確屬於 option value,請使用可攜的 = 寫法:

dbcli query "SELECT * FROM events" --fields=-raw_response,-request_payload
dbcli query '{}' --collection raw_logs --fields=-raw_response,-request_payload

每次執行只能使用 inclusion 或 exclusion 其中一種語法,不可混用。空白 list、空白項目與重複 path 都會被拒絕。支援 profile.name 這類 dotted path;inclusion 輸出會遵循指定順序。MongoDB inclusion 預設排除 _id,只有明確指定 _id 時才會保留。結果中不存在的欄位會回傳 null 而非報錯,因此拼錯的欄位名會得到一整欄 null——在把「全 null」解讀成有意義之前,先用 dbcli schema 核對欄位名。

SQL 會在 query 執行完畢後對回傳 rows 套用 projection。MongoDB 會將 projection 下推至 find 或 aggregation pipeline 以減少傳輸資料量,之後再於 blacklist masking 完成後正規化回傳 rows。Redis 與 Elasticsearch query 不支援 --fields。

Blacklist 仍是最終權限邊界:--fields 無法顯示受保護欄位;field projection 只是調整結果形狀的便利功能,不是 security boundary。

寫入結果

insert、update 或 delete 在互動終端完成後,dbcli 會以自然語句摘要實際結果:受影響的列數與資料表、沒有符合資料、取消、dry-run 或失敗;只有真正執行的操作才會顯示耗時。stdout 被重新導向或透過 pipe 傳遞時,仍維持供腳本使用的穩定 JSON result envelope。在終端中使用 --format json 也可明確保留該 envelope。SQL、MongoDB 與 Redis 使用相同的 outcome 詞彙;dry-run 會回報 dry_run,不再回報 success。

真的改動到資料的寫入,摘要還會說明如何取回先前的值——就這三個指令而言只有備份一途:寫入成功之後 dbcli 不保留任何自動還原機制。失敗時改為指出 --recovery,也就是產生回復計畫、供 dbcli recover 讀取的旗標。沒有造成任何改動的結局(取消、dry-run、沒有符合的列)不會提到還原,因為沒有東西需要還原。

失敗同樣是散文,並且寫到 stderr、退出碼 1——不論是 blacklist 拒絕、--set 格式錯誤,或資料庫退回的語句。--format 在這三個指令只接受 text 或 json,其他值會在連線之前就被拒絕,而不是悄悄當成 text。

沒有加 --force 的寫入所詢問的確認內容——產生的 SQL、參數、刪除的危險警告,以及 y/n 問句——一律寫到 stderr,不寫 stdout。終端機兩個串流都看得到,所以手動執行時外觀不變;而擷取 stdout 的腳本無論有沒有 --force,拿到的都只會是一份 JSON 文件。dry-run 與取消都不會對資料庫送出任何寫入,但兩者仍會連線並讀取資料表 schema——因為它們要顯示的 SQL 沒有欄位清單就組不出來;完全不連線的預檢請改用 --plan。MongoDB 與 Redis 的寫入也會問同一個問題——它們是由指令直接送出而非經過 SQL executor,過去完全不問就執行——所以 cancelled 現在在每個引擎都可能出現。它們的提示顯示的是 dry-run 會印出的那段語句,沒有參數區塊,因為那些語句的值本來就寫在裡面。

--recovery 同時改變 insert / update / delete 失敗時寫到 stdout 的內容:失敗時 recovery envelope 會取代 JSON result envelope,而不是接在它後面,因此解析 stdout 的呼叫端無論如何都只會拿到一份文件。沒有加這個旗標時,失敗仍然是印出 status: "error" 的 result envelope 並以 1 退出。

insert、update、delete 支援 --plan,可在不連線資料庫的情況下對即將執行的寫入操作執行靜態風險分析。此預檢現已支援 SQL(postgresql、mysql、mariadb)、MongoDB、Redis 與 Elasticsearch。預檢會套用該連線的 permission、blacklist 與該引擎的 schema 快取。--format text(預設)輸出人類可讀結論,--format json 輸出完整 QueryRiskResult。分析器回傳 BLOCK 時 exit code 仍為 0;設定 / 連線系統 / 無效 DSL 等錯誤才會 exit 1。--plan 與 --dry-run 互斥。

各引擎在 MVP 中的保守限制:

引擎BLOCK 範例WARN 範例
SQLUPDATE/DELETE 缺 WHERE、DDL、表黑名單Schema 快取缺失、引用黑名單欄位
MongoDB空 filter {}、$set/$unset 以外的更新運算子、$wherefilter 沒有 _id、廣泛的 $in/$regex/$gte、schema 缺失
Redis通配符 * 目標、黑名單 key/欄位Pattern 目標(例如 user:*)、update 缺欄位資訊
Elasticsearchupdate/delete 沒有 _id、黑名單 index/欄位Insert 沒有 _id、schema 缺失

BLOCK 表示預檢偵測到不安全的意圖。實際執行寫入前仍建議使用 --dry-run 再次確認。

儲存查詢管理

在版本控制中維護複雜的查詢片段,支援參數化與視覺化元資料。

健康度與診斷

doctor
驗證執行環境、連線能力及配置完整性;JSON 的設定缺失錯誤會包含結構化 remediation 指令與風險等級。doctor --format json --remediation 會為 blacklist 覆蓋、schema 更新與大型資料表提供僅供候選的計畫。SQL 大型資料表候選會先執行 dbcli plan;MongoDB/Elasticsearch 候選先以 dbcli schema 預檢,再由人工確認 bounded dbcli query。Doctor 不會變更設定、blacklist 規則或資料庫資料。
report
產生涵蓋健康度、效能及容量的全面報告。
recover
針對常見失敗模式執行自動化修復計畫。
audit
讀取每連線的 audit log(.dbcli/audit/<conn>.jsonl);使用 --for-agent 取得 handoff JSON。

Runtime、machine output 與 backfill artifact

doctor 會回報 runtime identity(launcher/source、runtime 與 package version),並在 bundled runtime 與 package version 不一致時標示,提供明確的 dbcli upgrade remediation。機器可讀指令(--format json 與其他非人類格式、--for-agent 或 --recovery)保持 stdout 僅含 payload:update 與 skill-update notice 都會被抑制。人類可讀 notice 寫到 stderr,且每個 CLI session 同類提示只會出現一次。

backfill artifact 將刻意受限的 JSON source catalog 轉為可檢閱、僅 dry-run 的 artifact;它不會連線或寫入任一資料庫。Catalog 必須有 table、非空的 keyColumns、rows、verifyQuery 與 expect,且最多 1,000 筆 row。Target 必須是 PostgreSQL、MySQL 或 MariaDB;source identity 可以描述其他引擎。具名 v2 連線會記錄不含機密的 source/target identity 與兩者差異:

dbcli backfill artifact --source ./backfill.json \
  --source-use staging --target-use production

Artifact 包含產生的 UPDATE、每個 statement 的 plan 指令、blacklist/schema preflight、read-back 的 verify safe-backfill 指令與 rollback hint。請先檢閱;套用 SQL 是另一個需要明確人工確認的流程。以 --stdout 輸出 JSON,或使用 --out <path> 指定路徑。

ORM 定義漂移

diff --against-orm 會將應用程式 schema 與既有 SQL schema cache 比對。比對只讀 cache:不連線資料庫、不更新 cache,也不執行提案。若在意資料新鮮度,先執行 schema --format json;cache 為空時會 exit 1 並要求執行 dbcli schema。

五種輸入路徑涵蓋所有支援的格式:

輸入路徑傳入內容
Prisma一個 schema.prisma 檔。
Drizzle執行 drizzle-kit generate 產生的一個 PostgreSQL drizzle-kit v7 snapshot。
TypeORMschema:log 產生的 DDL;不解析 entity source file。
Sequelize對 scratch database 套用 migration 後取得的 schema-only dump;不解析 model source file。
可攜式 schemaRaw PostgreSQL/MySQL/MariaDB DDL(可多檔或 glob),或一個 normalized JSON 檔。
dbcli skill tasks plan orm-drift-review --param orm_path=prisma/schema.prisma --format json
dbcli diff --against-orm prisma/schema.prisma --format json

drizzle-kit generate
dbcli diff --against-orm drizzle/meta/0001_snapshot.json --orm-format drizzle --format table

bunx typeorm schema:log -d <path/to/datasource> > schema.sql
dbcli diff --against-orm schema.sql --orm-format typeorm --format table

# 先將專案的 Sequelize config 指向空的 scratch database
bunx sequelize-cli db:migrate
pg_dump --schema-only <scratch-database> > schema.sql
# MySQL 則改用:
mysqldump --no-data <database> > schema.sql
dbcli diff --against-orm schema.sql --orm-format sequelize --format json

dbcli diff --against-orm "migrations/*.sql" --orm-format ddl --format markdown
dbcli diff --against-orm schema.normalized.json --orm-format json --format table
dbcli diff --against-orm prisma/schema.prisma --recovery --format json

--against-orm 可重複使用,也接受逗號分隔路徑。DDL family 輸入(raw DDL 加上 typeorm、sequelize alias)支援真實 filesystem glob 與多檔;路徑會去重並以 deterministic order 排列,再當成單一共享且有序的 context 解析,所以後一檔案的 index 可連到前一檔案宣告的 table。Prisma、normalized JSON 與 Drizzle 都只接受一個檔案。Drizzle 輸入必須是 drizzle/meta/<NNNN>_snapshot.json 的 PostgreSQL drizzle-kit v7 snapshot。TypeORM schema:log 會輸出 schema:sync 將執行但不會實際套用的 SQL。Sequelize CLI 沒有通用的 db:migrate --dry-run,因此必須先對 scratch database 執行 migration,再用 pg_dump --schema-only 或 mysqldump --no-data 匯出。TypeORM entity 與 Sequelize model(.ts、.js、.mjs 或 .cjs)不會被解析;dbcli 會拒絕它們,並顯示精確的產生步驟。用 --orm-format prisma|ddl|json|drizzle|typeorm|sequelize 覆寫偵測;TypeORM alias 預設忽略 typeorm_metadata 與 migrations,Sequelize alias 預設忽略 SequelizeMeta。--ignore <globs> 可加入逗號分隔且大小寫敏感的 qualified table pattern,並以 --format json|table|markdown 選擇輸出。

類別意義
missing_in_dbError:ORM 預期的 table、column 或 index 不在 DB cache。
missing_in_ormWarning:cached DB object 不在 ORM 定義。
mismatch型別 family 不相容或 nullability 不同為 error;同 family 拼字、default 或 primary-key 差異為 info。
unmanaged忽略的資料表;會列出但不計分。

只有計分後的 error 會決定 drift exit code:有 error 時 exit 1,只有 warning、info、unmanaged 或 unparsed 時 exit 0;操作失敗仍會 exit 1,並可由 --recovery 包裝。PostgreSQL schema/table storage 會精確保留且大小寫敏感,因此 users 與 "Users" 可並存。解析 DDL 時,未引用的 Users 會折成 users,而引用的 "Users" 只會解析成 Users;quote state 來自 parsed identifier,絕不從顯示大小寫推測。重複 exact 或 resolved identity 會 fail closed。Drizzle snapshot 或 normalized JSON 檔本身不是合法 JSON,以及 normalized JSON 不符合契約時,都會 fail closed,並指名出錯的檔案與欄位,不會降級成 unparsed。不支援的 Prisma/DDL/Drizzle construct 會以 blocked: 原因保留在 unparsed,包含 Drizzle enum 與其他不支援的 snapshot construct。Drizzle column default 只接受 snapshot 中的 string、boolean 或有限 number;不支援的 default 會阻擋並省略該 column。PostgreSQL PARTITION BY 以及 MySQL/MariaDB 的 table engine、charset 與其他 table option 會產生 blocked: 項目,且不建立 managed table。Normalized JSON 要求每個 unparsed.reason 都以 blocked: 開頭。結構化 index 比對會去重;drift entry 會依 table/object/category/detail 的 Unicode code-point order 決定性排序,不受 locale 影響。

提案是 shell-safe 文字,預設仍為 dry-run。安全且未 qualified 的新增可產生 migrate;schema-qualified 或 CLI 無法無損表達的 index target 會升級至 migration-review,不會輸出損壞指令。Table、column 或 type positional 若以 - 開頭也會升級;以 dash 開頭的 option value 則使用 option-safe attached syntax,例如 --default=-1 或 --columns=--config,email。擷取 dry-run DDL,並把兩個精確值分別作為單一 quoted parameter 傳入:

dbcli skill tasks plan migration-review \
  --param "table=${exact_table}" \
  --param "ddl=${captured_ddl}"

完成審查前絕不加上 --execute。

資料驗證

驗證資料處理結果是否正確 — 擷取結果指紋,再針對指紋、第二個查詢或行內條件斷言不變量。僅支援 SQL 引擎(PostgreSQL / MySQL / MariaDB)。

指令用途
snapshot <query>擷取結果指紋(rowCount + 每欄 null/distinct/min/max/sum + 順序無關的 checksum)。預設檔案 .dbcli/snapshots/snap-<timestamp>.json;另有 --out、--rows、--stdout。黑名單欄位在源頭遮罩,快照可安全保存。作為 assert --against 的基準。
assert <query>驗證不變量;失敗時 exit 1,除非 --no-fail。--expect(rows>0、value==X、col:c not null、unique、between a and b、>= n)、--vs <query> --compare rows|value(對帳)、--against <snapshot> --tolerance <pct>(對基準的漂移;0 = 完全相符 checksum)。

assert --write-verification-artifact

使用 --write-verification-artifact 可在 read-back 斷言執行後,將結果佐證記錄(v1 VerificationArtifact JSON)寫入 .dbcli/verification/,提供可稽核的持久化軌跡。驗證文物會持續寫入 <cwd>/.dbcli/verification/(相對於當前工作目錄),不受 --config 檔案位置影響。

旗標:

旗標必填說明
--write-verification-artifact選用斷言執行後寫入 VerificationArtifact JSON。
--evidence-receipt <path>選用在 outcome 確定後原子寫入不含 SQL 或 rows 的安全 assert provenance。
--verification-subject <kind:name>是(啟用旗標時)被驗證的標的。允許的 kind:recovery、task-pack、assertion、migration、backfill、manual。
--verification-summary <text>否可讀的摘要文字。預設值:通過 → "Assertion verified the expected state.";失敗 → "Assertion did not verify the expected state."。

assert --expect ... --evidence-receipt <path> 不必搭配 --write-verification-artifact 也能寫入 receipt。只有各項檢查的布林 pass bits 會投影至既有 receipt schema,不包含原始 SQL 或結果值。要求 receipt 時,失敗斷言仍會寫入 failed receipt。--no-fail 只影響斷言的退出行為,不會把 receipt 改成成功;receipt 寫入失敗仍 exit 1。

evidence compose 可用 --receipt <path> 參照該明確且 workspace-contained 的 receipt;provenance 不是執行核准。

輸出合約:

計畫佐證 vs 結果佐證的區別。 dbcli skill tasks plan safe-backfill-verify 產生的計畫 JSON 包含一個 verification 區塊,其 status 為 "planned" — 這是計畫中的佐證定義,描述哪項檢查將在執行時進行。最後的 assert --write-verification-artifact 步驟才會產生結果佐證(status: verified 或 not_verified)。這兩者是不同的記錄;"planned" 不代表驗證已執行。

注意: Postgres 的 bigint 聚合函數(count(*)、sum())回傳的是字串,但比較時會依數值進行,因此 value == 0 不必轉型也成立。若要比對文字,把期望值加引號(value == "0")。

# 1. 規劃工作流(plan-only,計畫佐證)
dbcli skill tasks plan safe-backfill-verify \
  --param table=orders \
  --param query="UPDATE orders SET status = 1 WHERE status IS NULL" \
  --param verify_query="SELECT count(*)::int FROM orders WHERE status IS NULL" \
  --param expect="value == 0"

# 2. 手動 dry-run 寫入操作
dbcli update orders --where "status IS NULL" --set '{"status": 1}' --dry-run

# 3. 在既有寫入權限下執行寫入
dbcli update orders --where "status IS NULL" --set '{"status": 1}'

# 4. 執行最終斷言並持久化結果佐證
dbcli assert "SELECT count(*)::int FROM orders WHERE status IS NULL" \
  --expect "value == 0" \
  --write-verification-artifact \
  --verification-subject backfill:safe-backfill-verify

dbcli verify 執行驗證情境(safe-backfill、migration、rollback、constraint),永不執行寫入或 DDL。dbcli verification 檢視與管理這些情境產生的本機結果文物(位於 .dbcli/verification/)。

在 --after-write 執行完成後,可加入 --evidence-receipt <工作區相對路徑>,原子寫入連結至結果文物的安全 provenance receipt。receipt 不含 SQL、rows、credentials 或使用者路徑;它只記錄 provenance,不代表核准執行。預檢模式沒有已執行的結果文物,因此不支援 receipt。receipt outcome(succeeded/failed)與情境 status(verified、not_verified、indeterminate 或 blocked)分開;task-pack 的 planned evidence 仍僅限計畫。

inspect、report、schema、plan、lint、explain 與 impact assess 也支援 --evidence-receipt <工作區相對路徑>。receipt 只含固定 command subject、capability、時間、安全 context digest 與可選 correlation ID。要求 receipt 時,成功與失敗的指令結果都會產生 receipt;receipt 寫入失敗不會改變指令結果或 exit code。

verify safe-backfill

在不執行任何 UPDATE 的前提下,驗證安全 backfill 的正確性。預檢模式(預設)執行唯讀防護並印出實際的 after-write 指令;--after-write 模式重新執行防護、執行回讀斷言,並寫入驗證文物。

⚠️ verify safe-backfill 永遠不會執行 backfill 寫入。請先透過一般寫入指令執行已核准的寫入,再執行 --after-write。

預檢(Preflight):

dbcli verify safe-backfill \
  --table users \
  --query "UPDATE users SET status = 1 WHERE status IS NULL" \
  --verify-query "SELECT count(*)::int AS n FROM users WHERE status IS NULL" \
  --expect "value == 0"

After-write(寫入文物):

dbcli verify safe-backfill ... --after-write

檢視結果:dbcli verification show <artifact-id>。

預檢也會回顯預計執行的 update(你需自行執行的 --query),讓印出的內容成為此操作完整、可直接複製的紀錄。

結果狀態:verified(回讀符合 --expect)、not_verified(回讀與 --expect 相悖)、blocked(防護失敗 — blacklist、schema、plan,或 verify-query 非唯讀)、indeterminate(斷言已執行但無法產生可信的結論)。

即使其中一項失敗,四項防護仍會全部執行並分別回報結果。After-write 只有在四項全數通過時才執行斷言;持久化的失敗原因使用固定安全標籤,不會保存 driver 錯誤文字。持久化的自訂標籤與 SQL 證據也會遮蔽憑證、檔案系統路徑及 SQL 註解。

防護約束(fail closed):

💡 同一資料表的重複 backfill。文物的 subject 名稱預設為資料表(backfill:<table>)。當你對同一資料表執行多個不同的 backfill 時,請傳入 --subject-name <唯一標籤>,讓每次操作在 dbcli verification list 中都能獨立追蹤。

verify migration

針對 schema migration 進行預檢或 after-write 驗證。此指令永遠不會執行 DDL — 它分析提案的 ALTER TABLE、執行唯讀防護,並(在 after-write 模式下)在你於外部套用 migration 後記錄佐證。

⚠️ verify migration 永不執行 DDL。請先在外部套用 migration,再執行 --after-write 記錄佐證。

預檢(Preflight):

dbcli verify migration \
  --table users \
  --ddl "ALTER TABLE users ADD COLUMN verified_at TIMESTAMPTZ" \
  --verify-query "SELECT count(*)::int AS n FROM users WHERE verified_at IS NOT NULL" \
  --expect "value == 0"

在外部套用 migration 後:

dbcli verify migration ... --after-write
Option必填說明
--table <table>是Migration 影響的資料表。
--ddl <sql>是提案的 migration DDL,僅分析,永不執行。MVP 僅接受 ALTER TABLE。
--verify-query <sql>是migration 後回讀驗證用的純 SELECT。
--expect <expr>是回讀結果的斷言表達式。
--after-write否執行 migration 後的斷言並寫入 v1 文物。
--format <table|json>否輸出格式,預設 table。
--subject-name <name>否文物 subject 名稱,預設為資料表名稱。
--summary <text>否選填文物摘要覆寫。

預檢回傳 ready 或 blocked 並印出精確的 after-write 指令;ready 不等於 verified — 只表示防護通過。After-write 將回讀斷言對應至 verified / not_verified / indeterminate,防護失敗則為 blocked。MVP 中 CREATE TABLE、DROP TABLE、CREATE INDEX 及多語句 DDL 均會被阻擋。

ALTER TABLE 目標可為 table、schema.table 或 catalog.schema.table。每個區段可為簡單未加引號的名稱([A-Za-z_][A-Za-z0-9_]*)或加引號的識別字 — 雙引號("…")、反引號(`…`)或方括號([…]) — 因此 "user accounts" 或 "tenant-1"."orders" 皆可接受。無法在此契約下完整解析的目標(未封閉的引號、不支援的跳脫、或超過三個區段)會在 after-write 斷言之前被阻擋,原因明示「目標無法解析」,與 must match --table 的不符原因有所區別。

verify rollback

針對你於外部套用的 rollback 進行預檢或 after-write 驗證 — 可還原 schema migration(--kind ddl)或還原資料變更(--kind dml)。此指令永遠不會執行還原語句 — 它分析提案的 --statement、執行唯讀防護,並(在 after-write 模式下)在你自行套用 rollback 後記錄佐證。

⚠️ verify rollback 永不執行 rollback 語句。請先在外部套用 rollback,再執行 --after-write 記錄佐證。

Schema rollback 預檢(--kind ddl,單一 ALTER TABLE):

dbcli verify rollback \
  --kind ddl \
  --table users \
  --statement "ALTER TABLE users DROP COLUMN verified_at" \
  --verify-query "SELECT count(*)::int AS n FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'verified_at'" \
  --expect "value == 0"

資料 rollback 預檢(--kind dml,單一 UPDATE):

dbcli verify rollback \
  --kind dml \
  --table users \
  --statement "UPDATE users SET status = NULL WHERE status = 9" \
  --verify-query "SELECT count(*)::int AS n FROM users WHERE status = 9" \
  --expect "value == 0"

在外部套用 rollback 後:

dbcli verify rollback --kind <ddl|dml> ... --after-write
Option必填說明
--kind <ddl|dml>是還原語句種類:ddl(單一 ALTER TABLE)或 dml(單一 UPDATE)。
--table <table>是Rollback 影響的資料表。
--statement <sql>是提案的還原語句,僅分析,永不執行。
--verify-query <sql>是rollback 後回讀驗證用的純 SELECT。
--expect <expr>是回讀結果的斷言表達式。
--after-write否執行 rollback 後的斷言並寫入 v1 文物。
--format <table|json>否輸出格式,預設 table。
--subject-name <name>否文物 subject 名稱,預設為資料表名稱。
--summary <text>否選填文物摘要覆寫。

--kind 決定語句須符合哪一種文法,並復用同類 scenario 的防護:ddl 復用 verify migration 的 ALTER TABLE 契約(單一語句、目標須符合 --table);dml 復用 verify safe-backfill 的 plan 契約(僅 UPDATE、須含 WHERE、目標須符合 --table)。預檢回傳 ready 或 blocked;ready 不等於 verified。After-write 將回讀斷言對應至 verified / not_verified / indeterminate,防護失敗則為 blocked。文物會以既有的 subject kind 記錄此 rollback(ddl 為 migration,dml 為 backfill),並標記 command: verify rollback。MVP 中 ddl 僅支援單一 ALTER TABLE、dml 僅支援單一 UPDATE;尚未支援 INSERT/DELETE 還原。

verify constraint

預檢或 after-write 驗證「資料完整性不變式是否成立」 — 外鍵一致性、NOT NULL 覆蓋率、唯一性或自訂違規查詢。此指令永遠不會執行寫入 — 只執行唯讀 COUNT(*) 違規查詢,並(在 after-write 模式下)記錄佐證。

⚠️ verify constraint 永不執行寫入或 DDL 語句。請在變更前執行 preflight,變更套用後再以 --after-write 記錄佐證。

以 --check 選擇四種檢查類型:

FK 預檢(在 migration 前驗證無孤兒 orders):

dbcli verify constraint \
  --table orders \
  --check fk \
  --column customer_id \
  --references customers.id

NOT NULL 預檢(驗證欄位已完整填寫):

dbcli verify constraint \
  --table users \
  --check not-null \
  --column email

在外部套用變更後:

dbcli verify constraint --table orders --check fk --column customer_id \
  --references customers.id --after-write
Option必填說明
--table <table>是受檢資料表。
--check <kind>是限制類型:fk | not-null | unique | custom。
--column <name>是(fk/not-null/unique)受檢欄位。not-null/unique 可重複;fk 為子欄位。
--references <table.column>是(fk)FK 父查詢的參照 <table>.<column>。
--violation-query <sql>是(custom)唯讀 SELECT,回傳單一整數違規筆數。
--allow-preexisting否無回退模式:count ≤ --baseline 時視為驗證通過。
--baseline <n>否preflight 量測的基準違規筆數(搭配 --allow-preexisting)。
--after-write否重新計算違規筆數並寫入 v1 文物。
--format <table|json>否輸出格式,預設 table。
--subject-name <name>否文物 subject 名稱,預設為資料表名稱。
--summary <text>否選填文物摘要覆寫。

預檢回傳 ready 或 blocked;ready 不等於 verified。After-write 將違規筆數對應至 verified(violations ≤ threshold)或 not_verified(violations > threshold)。預設 threshold 為 0(嚴格模式)。啟用 --allow-preexisting 時,threshold 為 preflight 量測的 --baseline 筆數 — 只要 after-write 筆數不超過預既存水準即視為通過。查詢錯誤回傳 indeterminate,防護失敗回傳 blocked。文物使用 subject.kind = 'table'、command: verify constraint。MVP:僅限 SQL 引擎;FK 僅支援單一欄位;永不執行寫入。

verification — 檢視與管理驗證文物

dbcli verification 操作寫入於 <cwd>/.dbcli/verification/ 下的文物。它不連線資料庫、也不寫入稽核記錄。list、show、summary 為唯讀的檔案系統檢視;prune 為本機生命週期指令,預設為 dry-run,僅在帶上 --execute --force 時才刪除檔案。儲存根目錄為目前工作目錄,與 --config 位置無關。

狀態值:verified、not_verified、indeterminate、blocked。
Subject kind:recovery、task-pack、assertion、migration、backfill、manual。

若 .dbcli/verification/ 目錄不存在,回傳空結果並以 0 退出。list/summary 執行時會略過格式有誤的檔案(可透過 --include-invalid 及 summary 的無效計數觀察);對格式有誤的檔案執行 show 時,會以 1 退出。

prune 預設為 dry-run。--keep-latest(預設 20)一律保護最新的 N 筆有效文物,範圍涵蓋所有 subject 與 status,且在套用 --status/--subject 篩選之前先行保護。只有同時帶上 --execute 與 --force 時才會刪除,且僅刪除 .dbcli/verification/ 內符合 verification-*.json 的一般檔案;execute 模式的表格輸出會逐一列出已刪除與略過的檔案。

dbcli verification summary --format json
dbcli verification list --status verified --subject backfill:safe-backfill-verify
dbcli verification show ver_abcd --format json
dbcli verification prune --older-than 30d --format json
dbcli verification prune --older-than 30d --keep-latest 20 --execute --force

evidence — 建立離線證據包

dbcli evidence 把既有 verification artifact 與 audit entry 收斂為可供審閱或交接的、工作區內 canonical JSON 證據包。它不連線資料庫,也不會把 SQL、target、audit metadata、verification summary 或憑證複製進包內。Claim 是外部提供的陳述,會被明確標示;它不是 dbcli 的驗證裁決。

dbcli evidence compose --claims ./claims.json --verification ver_abcd --audit 1a2b \
  --receipt .dbcli/evidence/verify-receipt.json \
  --output .dbcli/evidence/review.json
dbcli evidence validate --file .dbcli/evidence/review.json --format json
dbcli evidence render --file .dbcli/evidence/review.json

Artifact 格式版本

Pack 與 receipt 帶有自己的 version,那不是 dbcli 套件版本(見 ADR-0013)。目前格式為 version: 2;dbcli 3.0.0 以前寫出的是 version: 1,而且是兩種彼此不相容的結構。validate --format json 會在 status 回報三種答案之一,只有第一種代表這個 pack 可以採信。

statustrust意義退出碼
current-validcurrent-valid目前格式、digest 通過、參照可解析。0
current-references-expiredcurrent-valid格式與 digest 都沒問題,但某個來源已不在。1
recognized-legacynot-current-valid舊版 dbcli 寫出的 pack。legacyFormat 與 producedBy 指出是哪一種;integrity 回報該格式自己的 digest 是否仍然吻合。參照不會被評估。1
unsupportednot-current-valid版本未知,或版本與結構不一致。1

舊格式的 pack 與 receipt 可讀、可驗 integrity,但永遠不會被當成 current-valid,而且沒有 migration:pack 的識別碼由 digest 推導而來,改寫它等於用舊 artifact 的來源憑證鑄造一份新的 artifact。需要目前格式時,請用現有證據重新 compose 一份。

dbcli proxy — 本機觀察型 Proxy

本機開發觀察型 Proxy — 將現有應用程式指向 Proxy 埠號,dbcli 便會將所有查詢轉發至真實資料庫,同時記錄查詢文字、延遲、位元組數、資料列數及錯誤事件。這不是正式環境閘道器。 僅限本機開發環境使用。

快速開始

# 明確指定上游 / 下游
dbcli proxy mysql --listen 127.0.0.1:3307 --target 127.0.0.1:3306
dbcli proxy postgresql --listen 127.0.0.1:5433 --target 127.0.0.1:5432

# 從具名連線推斷引擎與目標
dbcli --use local proxy --listen 127.0.0.1:3307

將應用程式的 DB host/port 改為 --listen 位址,憑證維持不變。Proxy 完全透明 — 應用程式的行為與直連相同。

選項

選項預設值說明
--listen—本機監聽位址(例如 127.0.0.1:3307)。必填。
--target—上游 DB 位址。未指定 --use 時必填。
--events.dbcli/proxy/events.jsonl僅追加的 JSONL 事件日誌路徑。
--slow-ms1000durationMs 達到此閾值(毫秒)的查詢會在 query_completed 事件中標記 slow: true(並印出終端警告)。
--redactnonenone 保留原始 SQL 文字;literals 遮罩字串與數字字面值。
--formattext終端輸出格式:text 或 json。

事件日誌(JSONL)

每筆完成的查詢會在事件日誌中附加一個 JSON 物件:

{"version":1,"type":"query_completed","timestamp":"2026-06-04T12:00:00.000Z","engine":"mysql","sessionId":"pxy_1","queryId":"qry_pxy_1_1","client":"127.0.0.1:54321","target":"127.0.0.1:3306","sql":"SELECT * FROM users WHERE id = ?","statement":"SELECT","tables":["users"],"durationMs":4,"requestBytes":42,"responseBytes":318,"rowCount":1,"error":null,"tags":[]}

隱私

SQL 文字一律儲存於事件日誌。結果資料列永不儲存。 使用 --redact literals 可在記錄前遮罩 SQL 中的字串與數字字面值(例如 WHERE id = ? 取代 WHERE id = 42)。

離線分析事件日誌

dbcli proxy analyze — 離線分析擷取的事件日誌(不連 DB)。--format json|text、--top、--slow-ms、--n-plus-one、--no-include-rotated。輸出總覽、各查詢指紋統計、最慢查詢、錯誤分群、熱點表、N+1 嫌疑。

每個可行動的區塊都附帶機器可讀的後續步驟,讓 AI agent 能從「診斷」直接推進到「修正」:

建議流程:跑 proxy analyze 後,逐一針對每個發現讀其 hints、執行其 suggestedCommands 蒐集 schema/查詢計畫證據,再套用修正。text 格式會把這些彙整成 SUGGESTED COMMANDS 與 HINTS 區段;JSON 格式則把建議附在各發現上。建議僅以字串輸出——proxy analyze 不會自動執行。若 proxy 啟動時帶了 --redact literals,範例 SQL 會含 ? 佔位符;執行指令前請先填回真實值。

限制(v1)

QueryLens — Proxy 查詢分析

QueryLens 是 dbcli proxy analyze 的可分享 Markdown 報告格式,可將 proxy 事件日誌轉為本機查詢分析報告;它不是完整的資料庫協定分析器。

分析時不需要資料庫連線,也不需要 dbcli connection 設定:它只讀取磁碟上的 JSONL 檔案。QueryLens Markdown 報告會在分析前遮罩 SQL literal;擷取時也請使用 literal 遮罩,避免事件檔本身保留應用程式的值。

快速開始

# 1. 擷取已遮罩的本機開發流量 JSONL 日誌。
dbcli proxy mysql --listen 127.0.0.1:3307 --target 127.0.0.1:3306 \
  --events .dbcli/proxy/events.jsonl --redact literals

# 2. 在本機產生 QueryLens Markdown 報告;不會連線至資料庫。
dbcli proxy analyze \
  --events .dbcli/proxy/events.jsonl --format markdown

# 若要供其他工具或 CI 步驟使用,保留既有 proxy JSON 報告。
dbcli proxy analyze \
  --events .dbcli/proxy/events.jsonl --format json

執行應用程式時,將它指向 proxy 的 --listen 位址。停止 proxy 後,請在 repository root 對產生的日誌執行分析指令。

分析選項

選項說明
--events <path>proxy JSONL 事件日誌的路徑。
--format markdown|json|textmarkdown 產生已遮罩的 QueryLens 報告;JSON 與 text 保持既有 proxy 分析輸出。
--top <n>將排名發現限制為前 n 項。
--slow-ms <ms>報告判定慢查詢使用的時間門檻。
--n-plus-one <n>判定 N+1 候選項目使用的重複次數門檻。
--no-include-rotated只分析指定日誌,不納入輪替後的同層日誌。

QueryLens 只會分析它能讀取的 proxy 事件;請勿把結果視為已完整擷取所有資料庫協定或查詢形式的證據。將發現當作調查線索,修改正式程式碼前仍應確認查詢、schema 或執行計畫。

進階工具

指令用途
shell啟動互動式 REPL,支援補全與 SQL 高亮。
migrate <action>執行 SQL 資料表與索引的建立、修改或刪除等 DDL 工作流。
skill --install為 Claude、Gemini、Antigravity、Codex 等 AI 工具安裝唯一、英文的代理指引。
skill context將快取的 schema、連線與儲存的查詢元資料序列化為 LLM 優化的 XML/JSON/Markdown 格式,以供 AI prompt 注入使用。
semantic validate / semantic context / semantic search / semantic drift / semantic migrate / semantic draft validate驗證、輸出、搜尋、檢查漂移、僅輸出遷移後的可版控業務語彙,或安全驗證明確提交的不受信任 query draft;只對照本機快取、已經 blacklist 過濾的 semantic 證據。離線且唯讀。
contract validate / contract context / contract search / contract drift驗證、輸出、搜尋或檢查可選的已審閱 dbcli.contracts.json 是否仍符合 semantic 證據。離線且唯讀;普通 agent context 只會納入 approved 契約。
design init / design validate / design render / design diff / design propose明確建立本機 starter 檔,驗證或輸出可版控的 SQL 資料庫設計,與本機 SQL schema cache 或 ORM 定義比較,並產出只供審查的變更計畫。不會執行 DDL 或暗中寫入設計檔。
impact assess將設計變更相對於本機 schema cache 或 ORM artifact 的已知影響寫成離線報告;不會連線、執行 SQL,亦不會宣稱覆蓋完整。
skill tasks管理診斷、稽核與資料庫工作流等可重複專家任務包。
completion為 bash、zsh 或 fish 安裝 shell 自動補全。

Shell 自動補全

dbcli completion <bash|zsh|fish> 會輸出補全腳本;dbcli completion --install 會安裝它。已安裝的補全支援巢狀子指令,例如 dbcli queries list --<TAB>、dbcli migrate add-column --<TAB> 與 dbcli verify safe-backfill --<TAB>。在 option value 或位置參數之後也會維持 leaf command 範圍,例如 dbcli queries list --format json --<TAB>。

在 dbcli shell 中,指令補全會依照目前的指令範圍運作,因此新增的指令(q、queries、inspect、verify、proxy、snapshot 等)會自動補全並可被執行。

dbcli completion --install 採用標記區塊管理:它只會在 shell 設定檔寫入單一管理區塊,重新執行時會「取代」該區塊,而不會重複新增。

內建任務包 analyze-table-perf

唯讀(plan-only)的 task pack,吃必填的 table 參數,依序執行 blacklist list → schema <table> --format json → guide index-usage --format json。dbcli inspect 會針對近期活動中最熱門的資料表自動建議它。另也內建多個唯讀套件 — audit-permissions、safe-backfill、schema-drift-review、orm-drift-review、design-review 與 connection-health。design-review 會驗證/輸出 artifact、更新 cache,並產出只供審查的 proposal;絕不套用它們。每個 pack 的 requirement 都是 capability ID;輸出計畫前會依本機 engine、permission 與 agent-mode context 檢查,未知或不可用的 requirement 一律 fail closed。用 dbcli skill tasks list 瀏覽所有 task pack。

在 DBCLI_AGENT_MODE=1 下,只要符合 engine 與 permission 要求,內建 pack 就能產生計畫。讀取黑名單的 pack 使用唯讀能力 blacklist.read(blacklist list);connection-health 不需要黑名單能力。blacklist.manage 仍屬於設定修改能力,在 agent mode 下不可用;connection.init、connection.select 與 connection.rotate-credential 也維持不可用。產生計畫不會執行任何步驟,也不代表取得執行授權。

業務語意脈絡

在專案根目錄放置可檢閱的 dbcli.semantic.json,描述業務模型、可見欄位、別名、relationship 與由 saved query 支援的 metric。可在不連線資料庫的情況下驗證:

dbcli semantic validate --format json
dbcli semantic context --format json
dbcli semantic search purchases --kind model --format json
dbcli semantic drift --format json
dbcli semantic migrate --to 2 --format json
dbcli skill context --format json

如何被主動發現。 已安裝的 dbcli skill 會在需求含有業務別名、metric、反覆使用的術語或 relationship/join 意圖時,要求 agent 先檢查 skill context。若存在已驗證的 semantic 區塊,就以它作為受治理詞彙;否則退回已過濾 blacklist 的 schema,並告知你這項可選能力能讓後續需求保持一致。除非你明確要求,dbcli 不會建立或修改 dbcli.semantic.json。

v1 持續相容;v2 新增已宣告且可見 model 欄位間的 relationship。驗證器會拒絕不存在於快取可見 schema 的資料表或欄位(包含 blacklist 的物件),以及 query 並非可用 @saved-query 的 metric。semantic search <terms...> 為離線且 deterministic 的搜尋;可用 --kind 與 --limit 1-100(預設 20)。它只輸出受治理的 metadata,並從自由文字結果移除 blacklist 名稱。semantic drift 在 stale、invalid 或 schema cache 不可用時以非零結束;migrate --to 2 只輸出 JSON,絕不寫回來源檔。

語意契約。 在 semantic 檔旁加入可選、可檢閱的 dbcli.contracts.json,為受治理的業務術語補上 owner 與描述性的 evidence expectation。檔案必須使用版本 1、canonical contract name、canonical model: / field: / relationship: / metric: subject、draft、approved 或 deprecated status,以及 none、receipt-required 或 verification-required evidence policy;不得包含 SQL、憑證、受保護識別字或可執行規則。

dbcli contract validate --format json
dbcli contract context --format json
dbcli contract search customer --format json
dbcli contract drift --format json

這些指令不會連線或執行查詢。context、search 與 skill context 只會輸出有效且 approved 的契約;draft 與 deprecated 術語保留為本機審閱產物。缺少契約檔不會改變一般 semantic context;但明確指定的缺檔或無效檔案會 fail closed。contract drift 會區分 valid、stale、invalid 與 unavailable 的本機證據:subject 不屬於四種 canonical 形式時判為 invalid,形式正確但已不存在的 subject 才是 stale。診斷只會指出出問題的屬性或 subject 位置,不會複述產物或本機設定中被拒絕的 key、值或路徑。

Agent query draft。 先把已檢閱的 dbcli semantic context --format json 輸出交給外部 agent;provider 帳號、憑證、prompt 與其他 agent context 都留在 dbcli 外。agent 只能使用這些 model 與 field,回傳不受信任的 QueryDraft:

{
  "version": 1,
  "questionHash": "<原始問題的-sha256>",
  "candidate": { "kind": "sql", "sql": "<已檢閱的唯讀-sql>" },
  "semanticReferences": ["model:<model>", "field:<model>.<field>"]
}

再只提交這個明確指定的檔案或 stdin 載荷:dbcli semantic draft validate --input ./draft.json --format json,或 external-agent | dbcli semantic draft validate --input - --format json。報告只含狀態、hash、canonical reference 與安全 violation code,絕不包含 candidate SQL。exit 0 代表有效、1 代表拒絕、2 代表本機 semantic 證據不可用。有效結果不是執行授權:請先檢閱原始 draft,再視需要另行呼叫 dbcli explain "<已檢閱的唯讀-sql>" 或 dbcli query "<已檢閱的唯讀-sql>"。驗證不會保存輸入,也不會呼叫這兩個指令;dbcli 不會取得 provider 憑證,也不會發出 provider 請求。

資料庫設計輔助工具

在新專案旁保留可檢閱的 dbcli.design.json。它描述目標 PostgreSQL/MySQL/MariaDB 的 model、field、key、relationship、index、access pattern 與設計決策;不含 SQL、憑證、資料列或 provider 設定。

# 只會寫入這個明確指定且不存在的路徑;請先編輯 starter 再驗證。
dbcli design init --output ./dbcli.design.json --dialect postgresql

# 兩個指令都離線且唯讀。
dbcli design validate --format json
dbcli design render --format mermaid
dbcli design diff --against-cache --format markdown
dbcli design diff --against-orm ./prisma/schema.prisma --format markdown
dbcli design propose --against-orm ./prisma/schema.prisma --format markdown

validate 對格式錯誤、缺少 primary key、無效 relationship endpoint/cardinality、relation type 不相容與不安全 index 採 fail-closed;access-pattern index 缺口屬建議性。render 只在驗證沒有 error 時輸出 JSON、Markdown 或 Mermaid。diff --against-cache 讀取既有的本機 cache(先執行 schema),不會開啟連線,並回報不同的 column、index 與 foreign key。diff --against-orm 完全在本機執行:可對照明確指定的 Prisma、DDL、Drizzle snapshot 或 normalized JSON artifact,不需要設定或資料庫連線;DDL 可使用 glob。兩種模式都唯讀,而且必須剛好選擇一個比較目標。propose 會為每一項變更加上 blacklist/schema 預檢、僅在既有 migration 能無損表示時才提供 dry-run 指令、回滾提醒與寫入後的唯讀驗證計畫;其他變更一律升級為 migration-review。它不會套用任何寫入。init 是唯一的寫入指令,必須指定 --output,且拒絕覆寫。這些指令不會呼叫 LLM 或查詢資料。外部 coding agent 可以起草檔案,但人仍應檢閱後再採用。

新專案工作流。 執行 design init,編輯明確建立的 artifact,再執行 design validate 與 design render。若已有 application model,資料庫尚未建立前即可用離線的 design diff --against-orm <path> 對齊 artifact 與 ORM。

既有資料庫演進。 先執行 blacklist list,以 schema --format json 更新 cache,再執行 design diff --against-cache 與 design propose --against-cache。檢閱計畫後,另外執行已核准的 migration;之後更新 schema 並重新執行相同的 diff。兩個 design 指令都不會寫入資料庫。

影響評估

dbcli impact assess --design ./dbcli.design.json --against-cache --events ./.dbcli/proxy/events.jsonl --output ./impact.json --format json --fail-on warn
dbcli impact assess --design ./dbcli.design.json --against-orm ./prisma/schema.prisma --output ./impact.md --format markdown --fail-on never

必須剛好選一種 baseline。指令只讀取本機 design/cache/ORM、semantic contract、saved-query 名稱、verification metadata,以及可選且已審閱的 dbcli.data-access.json operation metadata。其 source path 必須已存在 workspace 內;dbcli 絕不讀取或解析那些 source file。可選的明確 --events 檔會先經 redaction-first 投影,只保留近期且安全的 table metadata;不會啟動 proxy、不會讀取 rotated log,也不會輸出 SQL、literal、error、session 或路徑。缺少、格式錯誤、過期、無法讀取或 redaction 失敗的 workload 證據會保留為 advisory coverage gap,且單獨存在時不會使 --fail-on warn 失敗。它不會連線、更新 cache、執行 SQL、讀取 query body,也不會輸出受保護識別字。缺少、無效或被隱藏的證據會明確列為 partial coverage gap;v1 永不回報 complete。--fail-on 只在報告寫出後改變 exit code(error、warn 或 never)。

safe-backfill-verify 任務計畫與 verification 區塊

執行 dbcli skill tasks plan safe-backfill-verify --format json 回傳的計畫 JSON 中包含一個 verification 區塊,其 status 為 "planned"。此區塊描述任務執行時將進行的回讀斷言 — 這是計畫中的佐證定義,而非執行結果。status: "planned" 不代表驗證已執行或通過,僅表示任務計畫知道要在執行時執行哪項驗證。

📊 互動式 HTML 儀表板

在指令後加上 --ui 旗標,即可產生獨立的 React 報表並自動開啟。

dbcli query "SELECT day, count(*) FROM sessions" --ui

豐富的視覺化組件(圖表、KPI)可透過 Snippet frontmatter 中的 visual: 區塊進行設定。支援的圖表類型為 line、bar、area、pie 四種;指定其他類型會在解析時報錯。

當 dbcli 的 lookahead 證明結果遭截斷時,dashboard 會在所有 KPI、圖表與 table 之前顯示警示並標出實際上限;blacklist 的遮蔽/省略通知也會顯示在同一區域。query HTML/UI、saved-query HTML/UI 與 HTML export 只要執行路徑有產生對應 metadata,都會沿用此行為。

執行溯源(saved query)

由 saved query 產生的 dashboard(dbcli q @name --ui 或 --format html)會帶上獨立的 Execution Traceability 區塊,位置在截斷與 blacklist 通知之後、KPI/圖表/table 之前。它就存在 HTML 檔案裡,收檔的人不需要 dbcli、資料庫或你的工作目錄也看得到。

Applied Limit 一定與上方的截斷警示一致;兩者矛盾的 dashboard 會在寫檔前就被拒絕。

溯源是封閉契約,不會攜帶原始查詢內容、參數預設值或列舉、憑證、endpoint、來源路徑、verification 的 query 與 expects、目標 index 或 collection 名稱,也不會帶上未顯示的資料列。同一份 allowlist 管的是整個內嵌 payload,不只是看得見的那一區:只有已顯示的資料列、applied-limit 與安全通知、provenance 物件,以及顯示用的 name/description 與引用已顯示欄位的圖表/KPI 定義會被序列化。無效、超長或未知的 metadata 會在寫出任何 HTML 之前被拒絕,失敗的 dashboard 不會留下半成品檔案。

直接查詢產生的 dashboard(dbcli query --ui、dbcli export --format html)行為不變,不會有溯源區塊。

引擎支援矩陣

功能 SQL SQLite MongoDB Redis ES
查詢能力 ✅ ✅ ✅ ✅ ✅
結構快取 ✅ ❌ ✅ ❌ ✅
儲存 Snippets ✅ ✅ ✅ ✅ ✅
資料寫入 ✅ ✅ ✅ ✅ ❌
結構變更 (DDL) ✅ ❌ ❌ ❌ ❌
互動式 UI ✅ ❌ ✅ ✅ ✅
查詢大小防護 ✅ ⚠️ ✅ ⚠️ ✅
黑名單強制 ✅ ✅ ✅ ⚠️ ⚠️
互動式 Shell ✅ ❌ ✅ ⚠️ ⚠️

SQLite 連線設定

SQLite 連線指名的是一個資料庫檔案,不是主機。唯一有意義的欄位是 file;沒有 host、port、user 或 password,因為存取控制來自作業系統本身的檔案權限,而不是網路認證這一層。最小可用的設定如下:

{
  "version": 2,
  "default": "local",
  "connections": {
    "local": { "system": "sqlite", "file": "./data/app.sqlite", "permission": "query-only" }
  }
}

file 和其他引擎的連線欄位一樣,可以寫成 {"$env": "APP_DB_PATH"} 參照;timeout / statementTimeout 這兩個選用欄位也同樣適用於 SQLite。

記憶體內的資料庫會被拒絕。:memory:、file::memory:,以及任何路徑帶有 mode=memory 的寫法,都會在設定檔解析階段就失敗,而不是等到連線時才發現。原因是:黑名單、稽核紀錄與 schema 快取,全部都以「連線」為範圍在追蹤狀態,而記憶體內資料庫每次啟動都是一份全新、不同的資料庫——參見 docs/adr/ADR-0038-an-in-memory-database-is-not-a-connection.md。

dbcli 不會建立資料庫檔案。file 指到不存在的路徑會在連線時失敗;路徑存在但不是 SQLite 資料庫也一樣會失敗,並且會明講失敗原因,而不是回傳看起來像損毀資料的結果。

query-only 是由資料庫引擎本身強制的,不是靠 dbcli。PostgreSQL 與 MySQL 是把語句包進唯讀 transaction 執行;SQLite 沒有這種 transaction 機制,所以 dbcli 改成直接以唯讀模式開啟資料庫檔案(SQLITE_OPEN_READONLY)——這個 handle 完全沒有寫入通道。這樣做有一個代價要講清楚:一個留有尚未回放的 write-ahead log 的資料庫,是沒辦法以唯讀模式開啟的,因為回放 log 本身就是一種寫入。dbcli 會直接點名是 WAL 造成的,而不是丟出一個看不懂的 SQLITE_CANTOPEN。參見 docs/adr/ADR-0037-query-only-on-sqlite-closes-the-door.md。

ATTACH 與 DETACH 在所有權限層級都會被拒絕,包含 admin,因為它們會碰到連線所指名之外的另一個資料庫檔案,這是連線邊界的問題,不是權限層級的問題。需要的話請改設定第二條連線。

建立 SQLite 連線

dbcli init --system sqlite 會先詢問連線名稱(除非已提供 --conn-name),再詢問資料庫檔案路徑與權限,不會問 host、port、user、password 或資料庫名稱,因為 SQLite 連線根本沒有這些東西。非互動模式下必須提供 --conn-name,並把路徑寫在 --file:

dbcli init --system sqlite --file ./data/app.sqlite --conn-name local --permission query-only --no-interactive

路徑會在任何東西被寫出去之前先檢查。 不存在的路徑、不是 SQLite 資料庫的檔案、以及任何形式的 in-memory 目標都會被拒絕,而 config.json 原封不動——dbcli 不會替你把資料庫建出來。

dbcli use 與 dbcli status 會在其他引擎顯示 host 的位置顯示檔案路徑,所以 SQLite 連線不會被印成一個空的 :0/ 端點。

dbcli doctor 檢查的是檔案而不是網路端點:檔案存不存在、讀不讀得到,以及——只有在權限允許寫入時——寫不寫得進去。query-only 連線指向一個唯讀檔案是正確的設定,doctor 會照實這樣報告,而不是報成失敗。

既有的 v1 設定無法升級成 SQLite 連線。 v1 這個格式早於這個引擎,所以 dbcli 會拒絕這個遷移,並指向 dbcli init --system sqlite。

目前可用的指令是 init、use、status、list、schema、query、q、queries、export、doctor、insert、update 與 delete。寫入走的是與其他 SQL 引擎相同的權限階梯與 --dry-run,生成的 SQL 使用 SQLite 標準的雙引號識別字與 ? 佔位符。REPLACE INTO 需要 data-admin 而非 read-write——它會先移除衝突的列再寫入替代值;PRAGMA、VACUUM 與 REINDEX 需要 admin。migrate、diff、shell、inspect 與 report 目前都還不支援 SQLite。

MongoDB 連線設定

dbcli init --system mongodb 現在預設走逐欄精靈,與 SQL 引擎一致:依序詢問 Host、是否為 SRV 網域、Port(SRV 時略過)、User,有填 User 才接著問 Password 與 authSource,最後是可選的進階步驟(replicaSet / tls)。貼上從 Atlas / Compass 複製的連線字串是明示的第二選項(「貼上連線字串(Atlas / Compass 複製的)」)。傳入 --uri 會跳過連線方式提問;非互動用法也必須提供 --conn-name。

Unix socket 連線字串(例如 mongodb://%2Ftmp%2Fmongodb-27017.sock/app)會保留原始 uri,包括帳密與選項,不拆成 TCP 主機及埠號。--uri 與互動貼上皆適用,原有的帳密明文儲存警告仍會顯示。

貼上的連線字串同樣會拆解。 互動貼上與 --uri 套用完全相同的規則(同樣的欄位對應;多組 host、帶機密 option、重複鍵、非 SCRAM 認證機制的字串同樣退回 uri 並警告帳密明文存放)。拆解成功後,終端會列出將寫入的每個欄位——密碼顯示為 <redacted>、options 逐鍵列出——然後直接進入權限選擇與連線測試,不再多一次確認。字串路徑帶資料庫名稱(或已給 --name)時不會問 Database name;否則必填且沒有預設值(互動終端下的 --uri 同樣如此,原本預設為 testdb)。字串無法解析(例如埠號不合法、authSource= 沒有值)時會印出不含原字串的錯誤並再問一次;直接按 Enter 則退回逐欄填寫。搭配 --use-env-refs 時,貼上的字串只會得到警告:帳密仍以字面值寫入,不會變成環境變數參照。

--uri 寫入前會先拆解。 以 --uri 傳入的連線字串(例如從 Atlas 複製的)不再整條存成不透明的 uri,而是拆成上述欄位設定:authSource、replicaSet、tls(含別名 ssl)進各自的正式欄位,mongodb+srv 對應 srv: true 且不寫 port,路徑上的資料庫名稱進 database(--name 可覆蓋),其餘 query 參數(retryWrites、w、appName 等)進 options。字串沒寫 authSource 就不寫該欄位,唯一例外:非 SRV、有帳號、路徑帶資料庫時會寫入 authSource: <路徑資料庫>,因為 driver 對這種字串以路徑資料庫認證,而沒寫 authSource 的欄位設定會預設成 admin。帳號與密碼會做百分比解碼。字串路徑沒有資料庫名稱時必須給 --name。

欄位設定表達不了的字串會原樣存進 uri(寫出的連線只含 system、uri、database),並警告帳密將明文存放於設定檔:含多組 host 的字串;帶機密 option 的字串(authMechanismProperties、tlsCertificateKeyFilePassword、proxyPassword,不分大小寫),拆進 options 會以明文落盤;同一 query 鍵出現多次的字串(不分大小寫,例如兩個 readPreferenceTags),因為欄位或 options 每個鍵只能存一個值;以及 authMechanism 不是 SCRAM-SHA-1/SCRAM-SHA-256 的字串(如 X509、GSSAPI、PLAIN、AWS)。警告會指出重複的鍵或該機制。明寫 SCRAM 的 authMechanism 照常拆解,留在 options。

--uri 不能與 --host、--port、--user、--password、--auth-source、--replica-set、--srv、--tls、--no-tls 並用(錯誤訊息會指出衝突的 flag),也不能與 --use-env-refs 並用:請拿掉 --use-env-refs 讓 dbcli 拆解字串寫入欄位設定,或改用 --env-* flag 逐欄給變數名稱。--name 可與 --uri 並用。

逐欄連線欄位(ConnectionConfig,src/types/index.ts):

--auth-source <db>、--replica-set <name>、--srv、--tls / --no-tls 都是 init 的 flag,CI 不必改用連線字串就能設定複本集、SRV(Atlas)與 TLS。--srv 會寫入 srv: true 且不寫 port;--tls 與 --no-tls 皆未給時不寫 tls,沿用 driver 預設。互動精靈中,已用 flag 給的值不會再問(SRV 提問,以及進階步驟的 replicaSet / tls)。搭配 --use-env-refs 時這些 flag 同樣生效,因為它們不是機密。

若設定同時存在 uri 與逐欄值(host / user / options),uri 仍會優先,逐欄值一樣被忽略——這點沒變,但 dbcli doctor 現在會針對這個情況發出警告,讓「改了欄位卻沒生效」可以被診斷出來,而不是原地困惑。doctor 也會在 srv: true 又同時指定非預設 port 時發出警告,因為 SRV 記錄自帶埠號。

逐欄模式下只填 user 沒填 password,現在會直接拋出錯誤,不再靜默降級成無認證連線。user、database、password 中的值在組成 URI 時皆會一致跳脫,帳號或資料庫名稱中含 @、/ 等字元不會再讓 driver 誤判 authority 的邊界。

連線失敗訊息依成因分類提示:認證失敗會提示檢查 user / password / authSource(Atlas 與多數自架環境為 admin);DNS/SRV 失敗會提示檢查 srv 設定與本機網路的 DNS 限制;TLS/憑證失敗會提示 tls 欄位與 CA 信任鏈設定。

MongoDB 寫入規劃器(運算子分層)

MongoDB 巢狀黑名單

dbcli 設定的 blacklist.columns[<collection>] 接受點分路徑,每一段都是 glob(*、?、字元類別如 [abc])。pass* 匹配 password,萬用字元不跨點號,pass* 不會匹配 user.password;中間段的萬用字元也合法,profile.*.email 匹配 profile.<任一段>.email。結尾整段的 * 保留原本的特殊意思:profile.tokens.* 涵蓋 profile.tokens 自己與其下所有後裔。無法編譯的規則(空路徑段、空字串、非字串)會中止操作並拋出錯誤,指出條目與原因,不再靜默略過;讀取遮罩、請求檢查與 insert / update(涵蓋所有 update 運算子)現在比對同一份已編譯規則。

blacklist.tables 對所有引擎都是 glob。 先前只有 Redis 與 Elasticsearch 展開萬用字元,tables: ["secrets*"] 在 MongoDB 與 SQL 完全不擋。現在 secrets* 會擋下 collection secrets_2026,SQL 資料表同名者亦然——這是 breaking change,真的叫 report* 的物件要寫成 report\* 才能回到字面比對。

指名受保護欄位的請求會被拒絕,而不是被遮罩。 遮罩比對的是文件回來時所在的鍵名,而在 MongoDB 裡那些鍵名是請求自己決定的:[{"$project":{"leak":"$password"}}] 把值放在 leak 底下回傳,[{"$group":{"_id":"$password"}}] 放在 _id 底下——而 _id 為了保住文件參照本來就被遮罩豁免。4.0.0 之前兩者在 query-only 下都跑得動。所以 password 出現在 pipeline、filter 或 update 的任何位置——欄位路徑、物件鍵、或單純一個字串——都會以 BlacklistRejection 拒絕,遮罩則留給一般的文件形狀。這個方向刻意過度拒絕:filter 的值剛好等於受保護欄位名({"status": "password"})也會被拒。

MongoDB schema 採樣

dbcli schema <collection> [--sample-size 100] [--sample-method random|natural]。random(預設)使用 $sample,驅動錯誤時退回自然順序。輸出包含巢狀 dot-path,附帶 presence(0..1)與命中黑名單時的 redacted: true。

MongoDB 儲存查詢

Mongo snippet 檔名以 .mongodb.sql 結尾,宣告 engine: mongodb 與 operation: find 或 operation: aggregate。以 dbcli q @<key> 執行;{{param}} 佔位符會以 JSON 編碼,被注入成運算子形狀的字串無法逃逸至運算子位置。

Redis:大小防護、黑名單與 shell(v1.21.0)

大小防護 — 無上限的讀取會自動加上界線:SCAN/HSCAN/SSCAN/ZSCAN 補上 COUNT 1000(較大值會被夾限);LRANGE/ZRANGE/ZREVRANGE 夾限 stop 至 1000 區間;ZRANGEBYSCORE 補上 LIMIT 0 1000;HGETALL/HKEYS/HVALS/SMEMBERS/KEYS 在 1000 筆截斷。結果帶有 warnings[](REDIS_SIZE_REWRITE / REDIS_SIZE_TRUNCATE)。dbcli query 會把每一則 warning 印到 stderr,且被裁切的回覆同樣會回報 truncated / limit_applied,與其他引擎一致,因此被截斷的回覆不會被誤認為完整。以 --no-limit(CLI)或 .no-limit on(shell)略過。

黑名單 — 規則以 Redis 原生 key glob(*、?、[abc]、[a-z])強制。dbcli blacklist table add 'secrets:*' 後,命中的讀取與寫入都會被拒絕(BlacklistRejection,稽核記錄含 matched_pattern);與規則重疊的 KEYS 與 SCAN MATCH 會被拒絕;裸的 SCAN 照跑,但受保護的 key 名會從回應裡濾掉;list 會濾掉黑名單 key。其中兩項在 4.0.0 之前並不成立:insert、update、delete 是逐字比對 key 名,所以本頁教的 secrets:* 只保護得了 dbcli query,dbcli delete secrets:api_key 照樣穿過去;而 SCAN 的 MATCH 從來不被檢查,於是 query-only 就列舉得出受保護的 key 名,同樣的事 KEYS 卻需要 admin。有設黑名單時,dbcli 沒有 key 位置定義的指令現在會被拒絕,而不是不檢查就放行;沒設黑名單則一切照舊。

遮罩(Masking,v1.22) — key glob 黑名單是「拒絕」,遮罩則是「屏蔽」:命中的讀取會回傳 [REDACTED],讓 AI 代理仍能執行指令但看不到敏感值。在 dbcli 設定中加入選用的 redis.mask 區塊,例如 { "redis": { "mask": [{ "keyPattern": "user:*", "fields": ["password", "token"] }, { "keyPattern": "secret:*" }] } }。有 fields → 只遮罩這些 hash 欄位(HGETALL/HGET/HMGET);無 fields → 遮罩整個值(GET/GETRANGE 及 hash 所有欄位)。遮罩涵蓋 GET/GETRANGE/HGETALL/HGET/HMGET/HVALS。拒絕優先於遮罩 — 同時命中 blacklist 與 mask 規則的 key 會直接被拒絕,不會進入遮罩流程。

Shell — Redis 連線執行 dbcli shell 會開啟單行 REPL,具備歷史、tab 補全(指令 + key 前綴)與 .no-limit on/off 切換。指令直接輸入,毋須結尾分號(例如 GET mykey)。

Elasticsearch:互動式 shell(v1.22.0)

Elasticsearch 連線執行 dbcli shell 會開啟專屬的 Kibana Dev Tools 風格 REPL。輸入請求行 <METHOD> /<path>,接著可選的多行 JSON body,再以空白行送出整個區塊。回應以美化後的 JSON 呈現。

es> GET /_cat/indices
        (空白行送出)

es> POST /users/_search
... {
...   "query": { "match_all": {} }
... }
        (空白行送出)

以讀取為主。 Index 層級黑名單會在前端直接拒絕受保護的 index;任何 _search 請求若 body 未指定 size,會自動上限為 1000 筆 hits。空白行送出當前區塊;Ctrl+C 取消進行中的區塊;Ctrl+D 或輸入 exit / quit 離開 shell。

Elasticsearch:匯出(v1.22.0)

Elasticsearch 連線執行 dbcli export 可將文件匯出為 JSON、JSONL 或 CSV,支援兩種形式:

# 1. 匯出 search DSL 的命中結果 — 需要 --index
dbcli export '{"query":{"match":{"status":"open"}}}' --index orders --format json

# 2. 透過 match_all + scroll 匯出整個 index — 直接把 index 名稱當作查詢傳入
dbcli export orders --format jsonl --output orders.jsonl

🤖 AI 代理整合

Dbcli 設計成 AI 代理的「資料庫驅動程式」:

受限跨引擎 context(版本 2)

將資料庫 metadata 交給外部 agent 時,使用 dbcli skill context --context-version 2 --format json。版本 2 是穩定的 agent contract,離線執行,絕不開啟連線、建立 adapter、掃描 Redis key、讀取文件或讀取專案 source。Agent 可在自身 workspace safety rule 下自行檢查專案 code,以補足 context 未包含的業務意義。

dbcli skill context --context-version 2 --format json

將此輸出和 agent 自行安全發現的 code context 交給 agent;不要把 source path 交給 dbcli,也不要要求 dbcli 解讀自然語言。將 permission 與描述性的 capabilities 視為限制,不是執行指令的授權。只能使用回傳的 resources 與 approved semantic/contracts metadata。若 metadata 缺少或回傳 gaps,不可猜測名稱、type、relationship、key 或意義:檢查允許的 project code,或要求補上證據。

版本 2 只輸出 safe fields:已設定的 engine 與 permission、blacklist policy、capability 的 command/status/sideEffectTier、resource 與 field ID/name/type、可見 SQL nullable/primary-key 與可見 foreign-key link、flattened Elasticsearch field path/type、已宣告的 Redis family/field、沒有 body/default 的 snippet metadata、沒有 source path 的 declared data-access metadata、approved semantic/contracts metadata、truncation count 與 gap。它絕不輸出 credentials、value/result、default/count、raw Elasticsearch mapping 或 setting、Redis key/value、query body/default,或 project source path/content。Blacklisted identifier 只會出現在 blacklist。

引擎版本 2 resources邊界
PostgreSQL、MySQL、MariaDB快取的可見 table、safe column 與可見 foreign-key link不含 default、count、index、comment 或被過濾 endpoint。
Elasticsearch快取 index 的 flattened field path 與 type不含 raw mapping、_meta、setting、script、analyzer、document 或 count。
Redisrepository 宣告的 dbcli.redis-context.json key family 與 field不做 discovery、scan,也不含 concrete key、live type 或 value。

Redis declaration 有意保持很小:context file ≤512 KiB;family ≤500;family name 為小寫 kebab-case,pattern ≤200 字元,含唯一且有效的 {placeholder},不得有 glob token、空白、control character 或 backslash;type 為 string、hash、list、set、zset 或 stream。僅 hash 與 stream 可宣告 field(≤100);description ≤1,000 字元,alias ≤20 個且每個 ≤100 字元。malformed、concrete、unsafe、blacklisted 或與 Redis field mask 重疊的 declaration 會直接失敗,不會輸出 partial model。

缺少 optional evidence 時會明確輸出:SQL_SCHEMA_UNAVAILABLE、ELASTICSEARCH_MAPPING_UNAVAILABLE、REDIS_KEY_FAMILIES_UNAVAILABLE、SEMANTIC_CONTEXT_UNAVAILABLE、SAVED_QUERIES_UNAVAILABLE、DATA_ACCESS_UNAVAILABLE 或 ALL_RESOURCES_FILTERED;truncation 另輸出 CONTEXT_TRUNCATED。已存在但無效的 evidence 會以相應的 INVALID_SCHEMA_CACHE、INVALID_SEMANTIC_CONTEXT、INVALID_SAVED_QUERY、INVALID_DATA_ACCESS_MANIFEST、INVALID_REDIS_CONTEXT 或 INVALID_RESOURCE_REFERENCE 失敗。MongoDB(及 unknown engine)明確要求 v2 時會回傳 UNSUPPORTED_CONTEXT_ENGINE。

省略 --context-version 時,維持 byte-compatible 的 version 1 JSON、XML 與 Markdown output。version 仍是 configuration metadata;v2 另加整數 contextVersion: 2。consumer 必須忽略未知的 optional v2 field;若要不相容地變更 required field 或 ID encoding,必須建立新的 context version。

業務請求的意圖確認

已安裝的 skill 支援三種當次請求的對話偏好,它們不是 dbcli 旗標或儲存的設定。Agent 不得先以「要不要讓我提問?」這類後設問題打斷使用者。

偏好Agent 行為
auto(預設)透過受治理的 semantic context 與 schema 證據釐清術語。僅在尚未解決的歧義會實質改變結果時,才用一小批精簡問題確認;否則說明假設後繼續。
confirm先說明預計採用的解讀,等待核准後才發出該任務的資料查詢。
guided以短而聚焦的問題建立請求內容,並在整個任務中保留已確認的答案。

例如「查昨天的銷售資料」在結果形狀(總額或明細)、指標定義、時區、訂單狀態/退款處理、分組或連線未知時仍有歧義。Agent 應摘要候選解讀,只詢問會改變結果的問題。若使用者明確要求自行判斷、不要再問,agent 會以 auto 模式繼續並說明重要假設。這個偏好絕不繞過 blacklist、schema、permission、dry-run、production 選取或寫入確認閘門。

何時 dbcli 比 MCP database server 或直接 DB client 更合適

沒有任何一種工具永遠勝出。可信任的人員在可拋棄的本機環境做一次性變更時,直接使用資料庫 client 是最短路徑;Agent host 需要以工具形式進行低風險互動探索時,MCP database server 很有價值。當同一個資料庫任務必須讓 Agent、人員、CI 與 incident runbook 都能安全且可重現地執行時,dbcli 才是更合適的邊界。

選擇最適合的情境取捨
直接 DB client可信任操作者以最小權限憑證進行一次性操作每個 client 與 script 都要自行重建安全、審查與證據慣例。
MCP database serverAgent host 需要透過 tool 介面進行低風險、互動式探索授權與稽核邊界由 host 與 server 定義;它通常不是 CI 或終端 runbook 使用的同一個邊界。
dbcli人員、Agent、自動化與復原流程必須共用同一份指令契約它刻意維持 CLI surface,因此 Agent 需要 shell command 授權,而非原生 MCP tool。

快速圖解:怎麼選

選擇能陪任務走到最後的操作邊界。

01

直接 client

可拋棄的本機實驗、合成資料,以及可信任開發者逐條看著 SQL 執行。

02

MCP database server

低風險的互動式探索;授權與稽核由 Agent host 和 tool server 所在邊界處理。

03

dbcli

真實資料、共享 fixture、migration、CI、incident 工作,或任何必須由另一個人重跑的結果。

本機起點

合成資料的快速實驗

切換到 dbcli 的時機

結果需要證據、審查或進入下一個環境

即使只是本機 vibe coding

本機開發改變的是爆炸半徑,不是基本的失敗模式。在可隨時丟棄、只含合成資料的資料庫上,當你會逐條看著 SQL 執行時,直接使用 client 完全合理;它更快,因為 Agent 可以立刻連線、探索並執行臨時 SQL。

代價是 client session 本身成了完整的安全契約。Agent 仍可能猜錯欄位名稱、查詢到本機的 production dump、對仍要保留的 fixture 執行無範圍操作,或留下當工作移到 CI 時沒有人能重跑的變更。資料庫 client 可能有自己的保護功能,但它們不會自動成為專案其餘部分使用的同一份政策與工作流。

dbcli 在 Agent 操作前加入一個很小、但刻意的停頓:先檢查保護範圍、讀取真實 schema,再預覽寫入。這些指令同時也是交接用的 artifact。開發者能把同一組步驟貼到 terminal、測試 script 或 PR 說明中,而不必重建 Agent 擁有的 client session 裡發生了什麼。

dbcli blacklist list
dbcli schema <table> --format json
dbcli update <table> --where "<predicate>" --set '<json>' --dry-run

這不代表每一條本機 SELECT 都值得一套儀式。結果不會進入任何操作流程的可拋棄實驗,就用直接 client。只要 Agent 會碰到接近真實的資料、修改共享 fixture 或 migration、需要可重跑的答案,或工作很可能進到 CI、staging、production,就使用 dbcli。它的價值不在於讓本機開發神奇地安全,而在於清楚地把快速實驗轉換成可負責工作流的時機標示出來。

在最後一種情境中,dbcli 有四個實際優勢:

  1. 操作者能審查並授權一次 invocation。 指令本身是具體交付物:執行前,人可以檢查選用連線、操作與 flags。像 Claude Code 這類可依 CLI 參數授權的 host,可形成實用的權限梯度——安全的探索指令能預先授權,查詢與寫入仍保留審查。MCP 權限通常以 tool 名稱為邊界;是否有更細的參數層級政策,取決於 host 與 server。
  2. 相同 guardrail 不依賴 Agent 仍會執行。 權限分級、blacklist 檢查、結果筆數限制、schema 探索、dry-run 預檢與機器可讀輸出都在 CLI path 上。CI job 與 incident responder 可執行相同指令並得到同樣的防護,不必仰賴特定 editor 或 agent session。
  3. 工作流會留下可操作的證據。 在支援的指令使用 --recovery,可產生結構化失敗封包並連結到 audit 紀錄。Saved queries 與 task packs 是團隊可 version、review、重用的檔案,而不是只存在聊天紀錄中的指示。
  4. 沒有 Agent 時工具仍可使用。 Terminal、Makefile、CI job 或 runbook 都能以有意義的 exit code 執行指令。因此 dbcli 是耐久的操作介面,而不只是 AI 整合。

例如,Agent 協助資料變更的安全基線,仍是一組人員或 CI 可重跑的步驟:

dbcli blacklist list
dbcli schema <table> --format json
dbcli update <table> --where "<predicate>" --set '<json>' --dry-run

當 MCP server 的對話式介面是主要價值,且它的授權、日誌與執行模型滿足環境需求時,使用 MCP。當風險允許以簡單性為優先時,使用直接 client。真正重要的是受治理、可審查的執行路徑,而非僅僅送出一條 query 的能力時,選擇 dbcli。

安全邊界:dbcli 是 defence in depth,不是資料庫授權的替代品。給 Agent 的資料庫憑證必須遵循最小權限。能修改自己 dbcli 設定的 process 可以提高宣告權限或改用其他 client;blacklist 與 dry-run 單獨無法讓該憑證變得安全。

🛠️ 開發者工作流

除了臨時查詢,dbcli 也針對「開發任務中牽涉資料庫」的常見情境而設計。Agent skill 內建了精簡的流程路由;當你自己操作 dbcli 時同樣適用:

以上全部繼承一般安全規則:優先 --format json、碰觸敏感資料前先跑 blacklist list、用 schema 確認名稱、寫入先 dry-run,且絕不列印 credentials 或 blacklisted 值。

🎯 使用情境速查

上面的開發者工作流是最小安全路徑。本節把具體情境對應到明確的指令路徑,依「你怎麼遇到它」分三類:具名任務(優先用已發布的 task pack)、跨領域操作需求、特定引擎工作。以下全部繼承安全基線(blacklist list → schema → 寫入先 dry-run)。

A. Task-pack 情境

當請求對應到某個具名工作流時,用 pack 來探索與產生計畫,而不是憑記憶拼步驟。所有 pack 都是唯讀 plan-only,且仍繼承 blacklist → schema → dry-run 規則。

slow-endpoint-investigation 吃必填的 query 與必填的 table,依佐證順序產生計畫:blacklist list → proxy analyze --format json → schema <table> --format json → explain "<SQL>" → guide missing-index-for "<SQL>" --format json。schema 排在 explain 與索引建議之前是刻意的——在沒確認資料表當下形狀的情況下讀執行計畫或索引候選,那是猜測。規劃只產出這些指令,別的什麼都不做:不開連線、不讀 proxy 事件與 schema、不執行 SQL,也不執行計畫中的任何一步。proxy 的觀察結果是本地看到的現象,不是端點變慢的已證實原因;索引候選是供審查的材料——這個工作流不建立索引、不套用 migration、不執行 DDL。缺 query 或 table 會以有界的錯誤失敗,不會輸出半份計畫。

dbcli skill tasks list --format json                       # 探索可用 pack
dbcli skill tasks plan <pack> --param k=v --format json    # 產生帶風險標籤的有序計畫
情境(使用者怎麼說)路徑Pack
「這條 SQL 很慢」(已有語句)skill tasks plan diagnose-slow-query --param query="<SQL>" → lint "<SQL>" → guide missing-index-fordiagnose-slow-query
「X 表很重/很熱」(已有表名)skill tasks plan analyze-table-perf --param table=<table>analyze-table-perf
「這個 API 端點慢」skill tasks plan slow-endpoint-investigation --param query="<SQL>" --param table=<table>(blacklist → proxy analyze → schema → explain → missing-index)slow-endpoint-investigation
全環境效能掃描report --section perf → guide slow-queryreport + guide
「給 agent 寫權限前先稽核」skill tasks plan audit-permissions(可選 --param table=<table>)audit-permissions
「線上 schema 跟 committed cache 一致嗎?」skill tasks plan schema-drift-review --param table=<table>schema-drift-review
「這份 ORM 定義跟 cached DB schema 一致嗎?」skill tasks plan orm-drift-review --param orm_path=prisma/schema.prismaorm-drift-review
「連線健康嗎?」skill tasks plan connection-healthconnection-health
「審這個動到 DB 的 PR」skill tasks plan pr-database-review;DDL/index 想法先過 migration-reviewpr-database-review / migration-review
「安全回填 X 欄位」skill tasks plan safe-backfill-verify --param table=<t> --param query="<UPDATE>" --param verify_query="<SELECT count(*)>"safe-backfill / safe-backfill-verify

Pack 解析順序為 local > shared > builtin:assets/tasks/(builtin)、.dbcli-shared/tasks/(團隊)、.dbcli/tasks/(本地覆寫)。計畫不會凌駕 blacklist、schema、dry-run 或確認要求——一次執行一步。

B. 跨領域情境

C. 特定引擎情境

SHOW GRANTS 認證內容去敏(MySQL/MariaDB)

SHOW GRANTS 結果一律在 MySQL/MariaDB 共用 adapter 回傳給任何消費端之前去敏,涵蓋正常與 native-read-only 執行。query、互動 shell 與 SQL 查詢 export,以及各命令支援的 JSON、CSV、表格與 HTML 輸出都適用。所有權限層級與 agent 模式均受保護,blacklist 為空也生效,沒有 bypass 選項。

條件式可執行註解會依建立連線時已讀取的伺服器版本判讀,不額外查詢版本。若無法取得該版本,且無法安全確定實際執行的語句,dbcli 會回報固定的安全錯誤,不回傳原始結果列。已確定不是 SHOW GRANTS 的語句維持既有行為。

可安全辨識的 grant 文字會把認證 payload 換成 [REDACTED],保留可判讀的帳號、角色、授權範圍與權限。辨識格式包含 IDENTIFIED BY、IDENTIFIED BY PASSWORD,以及 IDENTIFIED VIA / WITH 搭配 USING / AS、PASSWORD(...) 或以 OR 串接的多認證方法。所有認證參數都會遮蔽,包含空字串與 service name。可安全辨識且只有權限或角色的列保留原文字。

未知、不完整、語意歧義或型態異常的儲存格會換成 [OMITTED: grant text could not be safely redacted]。列數、欄名與結果結構保留,其他可安全辨識的儲存格照常呈現。被省略的儲存格不代表完整授權資訊。去敏結果供檢視使用,不能當成可重播的 grant SQL。

此保護只涵蓋 SHOW GRANTS,不掃描 SHOW CREATE USER、直接讀取 mysql.user 等系統表、任意 SQL 結果,或 SQL 輸入、既有檔案與歷史紀錄中原有的秘密。既有命令與政策限制仍適用;saved query 仍拒絕 SHOW GRANTS。

RDS CALL 拒絕與人工證據交接

Query-only 維持拒絕 CALL,包含 CALL mysql.rds_show_configuration,不建立 RDS 程序允許清單。實際 CLI 結果是本機政策拒絕;DBA 對 RDS 配置的觀察結論則維持 indeterminate(證據不足)。這是證據結論,不是新增 CLI 或 receipt 狀態或 schema。此拒絕不代表程序不存在、原生帳號權限不足、binlog 保留為零或備份不健康。缺少證據既不能判為成功,也不能判為配置不健康。

既有 Q024/Q025 觀察 記錄本機 MySQL/MariaDB 在預設 limit 與 --no-limit 下的 query-only 拒絕,未驗證 RDS 程序可用性、原生權限、唯讀交易相容性或結果處理能力。共享權限邊界維持不變。即使既有拒絕訊息提到 admin,也不應將升權視為補救。操作人員只能在既有授權內取得證據;dbcli 不會自動改用 admin 重試、呼叫原生客戶端或 AWS API,也不修改 RDS 資源。

請區分所需觀察:AWS 文件中的 binlog retention hours 是以小時計算的 binary log 保留設定,由 mysql.rds_show_configuration 觀察;BackupRetentionPeriod 則是自動 DB 快照保留天數。快照欄位不能證明 binlog 設定;沒有來源證明對所需觀察等價時,不得以平台 API 欄位替代。

人工交接時可複製以下模板,明確保留未取得的證據:

完整能力矩陣見 引擎支援。

🔁 Agent 修復工作流

此處只列三個最常見情境與通用流程。完整失敗代碼對照、Multi-turn --next、Risk gate 詳細語意與 Audit ↔ Envelope 反查請見 reference.md Recovery Cookbook。

當 query / q / insert / update / delete / export / schema / inspect / lint / diff --against-orm 帶 --recovery 失敗時,stdout 會輸出 RecoveryEnvelope JSON,並原子寫入 .dbcli/last-recovery.json。Agent 隨後用 dbcli recover 檢視,或 dbcli recover --apply 自動執行(預設只跑 readonly + dry-run 步驟)。

資料庫原生授權不足

失敗類型Adapter 代碼Recovery envelope
資料庫原生授權不足DB_PERMISSION_DENIEDerror.code: "PERMISSION_DENIED"、error.details.connectionCode: "DB_PERMISSION_DENIED"
登入驗證失敗AUTH_FAILEDerror.code: "CONN_AUTH_FAILED"
dbcli 本機政策拒絕不適用(本機 PermissionError)error.code: "PERMISSION_DENIED",不帶原生 discriminator

原生分類涵蓋 MySQL/MariaDB 的 ER_DBACCESS_DENIED_ERROR(1044)、ER_TABLEACCESS_DENIED_ERROR(1142)、ER_COLUMNACCESS_DENIED_ERROR(1143)與 ER_SPECIFIC_ACCESS_DENIED_ERROR(1227),包含只有 errno 的錯誤;PostgreSQL 則使用 42501(insufficient_privilege)。只有 SQLSTATE 42000 或訊息含有「access denied」不足以套用此分類。MySQL/MariaDB 1045、1698 與 PostgreSQL 28000、28P01 等登入失敗仍保留驗證失敗分類。

原生授權不足可能發生於建立連線或執行操作時。這個代碼不代表登入已成功;連線成功也不代表有權查詢物件。它不是傳輸故障,不會觸發自動重連或連線修復分支。

請確認目標資料庫/物件,以及該操作所需權限;必要時交由 DBA 處理,再重新執行原命令。錯誤只提供靜態人工指引,不建議更換憑證、執行 init 或放寬 dbcli 政策。本機 canWrite/canDestruct 旗標無法證明原生授權已修復。

Recovery 維持 schema v1:recovery: [],並省略 verify(不是 JSON null)。此路徑沒有可執行的修復或診斷命令,因此 recover --apply 不會進行修復。既有 v1 envelope 可繼續讀取,不需遷移;既有本機 PERMISSION_DENIED 復原行為不變。呼叫端應同時判讀上表的兩層錯誤碼,不能把所有 PERMISSION_DENIED envelope 都當成本機政策拒絕。

三個典型情境

CONN_REFUSED — 連線失敗

dbcli query "SELECT 1" --recovery
# envelope: recovery=[doctor, inspect] verify=doctor
dbcli recover --apply
# readonly 步驟全跑、verifyStatus=passed → 連線恢復

BLACKLIST_TABLE — 黑名單阻擋

dbcli query "SELECT * FROM audit_logs" --recovery
# envelope: blacklist list (readonly) + blacklist table remove (write)
dbcli recover --apply
# 步驟 2 動到本地 blacklist,被 gate 擋下 → exit 3
dbcli recover --apply --allow-write=readonly-cmd
# 確認解除遮罩,打開 local-write tier(仍不會碰資料庫本體)

SCHEMA_CACHE_MISSING — 快取缺失

dbcli inspect --require-schema-cache --recovery
# envelope: schema --refresh --force
dbcli recover --apply
# v2 多連線時 envelope 會自動帶 --use <name>
# 快取位置:.dbcli/schemas/<connection>/

Multi-turn 模式:給有自己 runner 的 agent

當 --apply 太粗(含 interactive 步驟、或想逐步審視):

dbcli recover --next --after-step 1 --result '{"status":"ok","exitCode":0}'
# stdout 過大時改用檔案(StepResultSummary JSON,stdout/stderr 各限末尾 4 KB)
dbcli recover --next --after-step 2 --result @/tmp/r2.json
# 跑完整個 plan 後:dbcli 回傳 kind: "done"
# 注意:--next 不會自動執行 verify,需自行再跑一次原失敗指令確認

連線錯誤的分支

針對連線類別錯誤(CONN_REFUSED、CONN_AUTH_FAILED、CONN_TIMEOUT、CONN_HOST_NOT_FOUND、CONN_UNKNOWN),envelope 會額外帶上 branches 對應表與 branchFork 描述。Agent 執行步驟 1(dbcli doctor --format json)後,將其輸出透過 --result 傳入;dbcli recover --next 會解析 doctor JSON,從四個分支(doctor-clean、doctor-config-missing、doctor-auth-error、doctor-network-error)中選出一個,並回傳該分支的第一步。回應會帶 branchId 與 branchDescription,agent 在後續 --next 呼叫應以 --branch <id> 回應。

旗標行為
--branch <id>走訪特定分支。分支發生後的所有 --next 呼叫皆需指定。

若 doctor JSON 無法解析或沒有關鍵字匹配,--next 會回落為線性的 recovery 計畫 — 分支永遠不會造成 --next 失敗。--apply 維持線性走訪 recovery,不使用 branches。

有一種連線類別錯誤不會分支:語句因超過 statement timeout 被伺服器取消(PostgreSQL SQLSTATE 57014、MySQL 3024、MariaDB 1969)。它回報為 CONN_TIMEOUT,並將 details.connectionCode 設為 STATEMENT_TIMEOUT,這個欄位就是它與真正連線逾時的分辨依據 — 連線是通的,因此計畫針對查詢本身(dbcli lint、dbcli explain,再以明確的 --statement-timeout <ms> 重跑),而不是去跑 doctor。因為步驟 1 不是 doctor,envelope 不會帶 branches 與 branchFork,也不會帶 verify — 這個錯誤除了重跑原語句沒有別的驗證方式,而原語句只有呼叫端有。三個步驟都帶 placeholder,因此 dbcli recover --apply 會回報 skipped-only 且不執行任何指令。

Audit ↔ Envelope 反查

recover --apply 退出碼速查

Exit意義
0全部步驟成功(若有 verify,verify 也通過)
1某一步執行失敗
2envelope 缺失、不存在或格式錯誤
3所有步驟都被 gate 跳過(widen --allow-write 或填上 placeholder 再試)

持久化驗證成果(選用)

在 recover --apply 加上 --write-verification-artifact 旗標,執行結束後會在 .dbcli/verification/ 下寫入一份有界的 VerificationArtifact JSON:

dbcli recover --apply --write-verification-artifact

疑難排解與錯誤參考

錯誤分類

dbcli 區分連線錯誤(server 沒起、認證失敗)與 SQL 錯誤(語法錯、table/column 不存在)。SQL 錯誤現在會印:

已分類的錯誤在巢狀 adapter 呼叫間會保留原始 code、message 與 hints,不再重複加上前綴。MySQL 8 的 schema introspection 也相容預設的 ONLY_FULL_GROUP_BY 模式。

當連線設定驗證失敗時,dbcli 會依該連線宣告的 system 對應到正確分支,逐一列出有問題的欄位路徑,而不是丟出整包巢狀的 union error tree —— 讓壞掉的 .dbcli 可以直接照欄位修,不必猜是哪個引擎分支。

有界的 CLI 錯誤輸出

需要連線的探索與讀取指令失敗時會以 exit code 1 結束,且錯誤只呈現一次。一般模式的 stderr 會輸出可讀訊息,以及存在時的穩定錯誤碼與可執行 hint;不會包含 JavaScript stack、bundle 原始碼片段或 source-code frame。需要診斷資訊時,請在指令前加 -v(或 -vv)以顯示 stack——旗標必須放在子指令之前(dbcli -v list,而不是 dbcli list -v)。寫入類指令(insert、update、delete)與 q 保留自己的在地化措辭,並同樣遵守這個 stack 開關。措辭依錯誤碼決定:只有真正的傳輸層失敗(ECONNREFUSED、ETIMEDOUT、AUTH_FAILED、ENOTFOUND、EHOSTUNREACH、CONNECTION_LOST、TOO_MANY_CONNECTIONS、TLS_ERROR、SERVER_NOT_READY、CONNECTION_REJECTED)才會被說成連線失敗——TABLE_NOT_FOUND、STATEMENT_TIMEOUT 這類語句層級的錯誤連得上伺服器,因此以自己的身分連同 hint 一起回報,而不是「無法連接到資料庫」。支援 --recovery 的指令仍只在 stdout 輸出既有 JSON recovery envelope,並抑制重複的人類可讀 stderr 訊息。

完整的 stdout 管線輸出

stdout 經過管線或重新導向時,dbcli 會在完整寫入後才結束。大型 JSON、CSV 與 HTML 結果經過 dbcli query --format json | jq ... 或 dbcli export ... | cat > result.json 等指令時仍會保持完整;成功的退出狀態不會代表只寫入部分 stdout buffer。

Query-only auto-LIMIT 範圍

dbcli 會在 query-only 模式對 SELECT 自動加 LIMIT 1000。不套用於:

查 information_schema 時用 --no-limit 關閉。

當上限由 dbcli 套用(query-only 預設值或明確的 --limit N)時,dbcli 會多取一筆作為 lookahead,藉此區分結果剛好 N 筆與實際還有更多資料。被截斷的 table 輸出會以 Rows: N (truncated; limit N) 結尾。只要上限由 dbcli 套用,JSON 一律包含 metadata.truncated 與 metadata.limit_applied;只有 lookahead 證明還有下一筆時,truncated 才是 true。使用 --no-limit,或 SQL、MongoDB pipeline、Elasticsearch request body 已自行指定上限時,這兩個欄位不會出現。CSV 會附加一行 # truncated; limit N 的註解。

透過 dbcli q 執行的已儲存 snippet 也用同樣方式回報。snippet 的 size guard 本身就有 1000 筆上限,現在這個上限會出現在 footer 與 metadata 中,不再需要使用者從整數列數猜測。

dbcli export 不回報截斷——而是直接拒絕執行。當匯出的資料被 query-only auto-limit 截斷時,會以 exit code 1 結束且不寫入檔案:

Export would silently drop rows — 1000-row auto-limit reached.
  Re-run with --no-limit to export everything,
  or --limit 1000 to accept the cap explicitly.

匯出的檔案沒有地方能記錄資料被遺漏——jsonl 是一行一筆文件、MongoDB 的 --format json 則是裸陣列——而 stderr 警告在重導向後就會消失。明確指定 --no-limit 或 --limit N 才能讓這個選擇變得明確,匯出才會繼續進行。

有界的 table cell

dbcli query 的 table 輸出預設會將每個序列化後的 cell 限制為 120 個 Unicode code point。可用 --truncate N 設定其他正整數上限,或用 --no-truncate 顯示完整的 table cell;兩個旗標不可同時使用。

計算上限前會沿用既有的 cell 序列化規則:null 與 undefined 轉成空字串,object 以 JSON 序列化,primitive 則使用其字串表示。序列化後的值超過上限時,dbcli 會保留前 N 個 code point,並附加例如 …(+3412 chars) 的標記。標記不計入保留值的 N 字元預算,數字則代表從序列化值省略的 Unicode code point 數量。

此行為只屬於 table formatting,絕不修改查詢結果中的 rows,因此 JSON 與 CSV 輸出仍保持無損。若在 JSON、CSV 或 HTML 輸出(包含 --ui)明確傳入 --truncate,dbcli 會回報錯誤,而不會靜默忽略。

Schema cache bootstrap

第一次 dbcli schema --refresh 不需 --force 即會寫入 cache。後續 refresh 偵測到既有 cache 有 diff 才會要求 --force。

文件維護與覆蓋範圍

Markdown (index.md) 與精緻版 HTML (index.html) 是同一份使用者指南的兩種呈現形式。維護時請把它們視為同一份文件契約。

對標規則

  • 每次新增或修改指令、旗標、警告、範例或支援矩陣時,同步更新 Markdown 與 HTML。
  • 使用相同順序的 doc-key 標記維持主題對標。
  • HTML 可使用卡片、圖示或短標籤,但語意必須與 Markdown 一致。
  • 英文文件變更時,同步更新繁體中文 Markdown 與 HTML。
  • 合併前執行 bun run docs:check。

必要覆蓋範圍

每個使用者可見功能都應說明設定、連線行為、探索指令、讀寫操作、Snippets、診斷、進階工具、引擎限制、AI 工作流安全與 HTML 儀表板行為。

# 驗證使用者文件對標
bun run docs:check

# 指令行為變更時執行 CLI 測試
bun test
範圍必要文件內容
安裝與設定安裝指令、初始化、環境變數與機密資訊安全處理。
連線多連線結構、列出、切換、單次 --use 與環境範例。
探索list、schema、inspect、status、輸出格式,以及 AI 代理查詢前應先檢查的時機。
讀取與寫入query、q、export、insert、update、delete、--dry-run 與寫入保護。
Snippetsqueries list/search/suggest/new、解析順序、參數與視覺化 frontmatter。
診斷與修復doctor、check、diff、report、guide、recover 與 --recovery。
進階工具shell、migrate、skill --install、skill tasks、completion 與支援的權限層級。
引擎PostgreSQL/MySQL/MariaDB、MongoDB、Redis、Elasticsearch 的支援差異與已知限制。
AI 使用黑名單檢查、schema 確認、dry-run/風險規劃,最後才執行。
HTML 儀表板--ui、匯出行為、圖表/KPI 設定,以及瀏覽器/報表預期。

查詢計畫檢視 — dbcli explain

跨 MySQL/MariaDB 與 PostgreSQL 用統一 row schema + severity 標籤呈現查詢計畫。

基本用法

dbcli explain "SELECT * FROM betting_logs WHERE settled_at >= '2026-03-01'"
dbcli explain @analytics/live-summary                 # saved query
dbcli explain --analyze "SELECT ..."                  # MariaDB ANALYZE SELECT / PG EXPLAIN ANALYZE
dbcli explain --format json "..."                     # 純 JSON
dbcli explain --bulk @queries.sql                     # 從檔案批次
dbcli explain --bulk @analytics/*                     # 對 saved query 做 glob

Annotations 規則

規則嚴重度觸發條件
full-scan紅MySQL type=ALL 或 key=NULL;PG Seq Scan
temp-table黃MySQL Using temporary
filesort黃MySQL Using filesort;PG Sort Method: external merge
cost-estimate-skew灰--analyze actual rows / planner rows > 10×
nested-loop-large黃PG Nested Loop 且 planner rows > 10,000

注意

靜態 SQL 顧問 — dbcli lint

lint 可分析 PostgreSQL、MySQL 或 MariaDB SQL,不會開啟資料庫連線、執行查詢、更新 schema,也不會套用 rewrite。需要 schema 的規則只會讀取分層 .dbcli/schemas/ 快取。

輸入與選項

dbcli lint "SELECT * FROM users WHERE email LIKE '%@example.com'"  # inline SQL
dbcli lint @analytics/live-summary                               # saved query
dbcli lint @queries.sql                                          # SQL 檔案
dbcli lint --bulk '@queries/**/*.sql'                            # 檔案系統 glob
dbcli lint --bulk '@analytics/*,@queries.sql' --format markdown  # 混合批次輸入
dbcli --use staging lint @analytics/live-summary --format json   # 命名快取

所有 schema 快取都位於 .dbcli/schemas/ 之下。v2 一律使用 .dbcli/schemas/<resolved-connection>/,包含設定的預設連線。根目錄 .dbcli/schemas/ 只供 v1/legacy 未命名快取使用。全域 selector 必須放在指令之前:dbcli --use <conn> lint …;它會選擇另一個具名 v2 slot。lint 不會退回讀取 config.schema,也不會為了補齊缺少的 metadata 而連線。

選項預設值行為
--format text|json|markdowntext文字、機器可讀 JSON 或 Markdown 報告。
--min-severity info|warn|errorinfo隱藏低於所選嚴重度的 findings。
--no-schema關閉不讀取 schema 快取路徑;跳過純 schema 檢查,但保留靜態 NOT IN NULL 檢查。
--bulk <input>無解析逗號分隔的 @file、@glob 與 @saved-query 混合輸入。
--recovery關閉失敗時輸出並儲存已連結的 recovery envelope。

規則

規則嚴重度回報條件
select-starwarn頂層 SELECT *;若單一資料表與快取資訊明確,可提供欄位清單草稿。
unanchored-likewarn以 % 開頭的 LIKE / ILIKE pattern。
missing-limit-offsetinfo使用 OFFSET >= 1000 的深度分頁;可考慮 keyset pagination。
non-sargable-wherewarnPredicate 的欄位側套用了函式或算術運算。
or-to-unioninfo不同欄位之間的頂層 OR;替代方案必須保留 identity 與 multiplicity。
subquery-to-joininfoIN (SELECT …) 可能適合語意等價的 EXISTS,或已證明唯一的 JOIN。
distinct-groupby-abusewarn簡單投影欄位完整涵蓋 GROUP BY 時,多餘的 DISTINCT。
implicit-castwarn經 schema 驗證的欄位/常值型別不符,可能讓索引失效。
not-in-nullablewarnNOT IN 右側為 NULL 或可能是 nullable:明確的 NULL、outer join 補出的 NULL、nullable subquery 投影,或已知可為 NULL 的 CASE/cast/aggregate 運算式。

not-in-nullable 專門描述 SQL 右側「NULL 污染 NOT IN」的風險。它會遞迴檢查投影、JOIN ON、WHERE 與 HAVING,且每個巢狀 SELECT/CTE/derived statement 都使用自己的 scope。當下 join 的 synthetic NULL 只會在該 join 的 ON 評估後套用;schema 宣告的 nullability 與更早完成的 join 仍會在 ON 內生效。左側欄位可為 NULL 並不屬於這條規則。若右側是 subquery,應以 IS NOT NULL 過濾投影值;在 correlation 與語意合適時,也可考慮 NOT EXISTS。除非 correlation、型別、qualified-column 解析與 rewrite 目標都明確,dbcli 不會自動執行此 rewrite。若 subquery 以直接條件或 AND 組合對完全相同的投影運算式套用 IS NOT NULL,則不會回報;aggregate 投影也會在 HAVING 套用同一證明。位於 OR 下或解析不明確時仍保守回報。

解析失敗時,九條規則都會列為 blocked: parse failed。--no-schema 會跳過 implicit-cast,並以 blocked: --no-schema 標記 not-in-nullable 無法執行的 schema 部分;明確 NULL 與其他結構上已知的 RHS 風險仍會執行。分層快取不存在時,無法執行的 schema 檢查會回報 blocked: schema cache unavailable (run dbcli schema)。有 confidence 標籤的草稿與 shell-safe 驗證指令都只供回報參考,絕不會自動執行。只有結構上已證明唯讀且不含明確 function/table-function call 或 session assignment 的 SQL 才會使用 dbcli explain --analyze;其他語句會退回 plain dbcli explain。identifier 折疊衝突及 CTE、derived、schema-qualified 或 database-qualified binding 都不會借用 unqualified cache facts。

缺失索引建議 — dbcli guide missing-index-for

分析單一 SELECT,結合真實的 EXPLAIN 計畫與既有索引,建議複合索引。唯讀。

基本用法

dbcli guide missing-index-for "SELECT ... FROM betting_logs b JOIN hoster_machines hm ON ..."
dbcli guide missing-index-for @analytics/live-summary
dbcli guide missing-index-for "..." --format json        # yaml(預設) | json | markdown
dbcli guide missing-index-for "..." --min-confidence medium

每個候選索引都會帶有 confidence(high / medium / low)與 reason;此工具不會斷言「你一定要建立」。函式/運算式欄位(例如 DATE(settled_at))以及無法解析的 SQL,會列在 warnings 之下。

限制