Codex CLI Hooks: 8 Lifecycle Event + 5 Pattern 2026
Codex CLI hooks là gì? Đây là cơ chế cho phép bạn chạy script tự động ở các điểm lifecycle của Codex CLI: trước khi tool gọi (PreToolUse), sau khi tool xong (PostToolUse), khi session bắt đầu (SessionStart), khi user submit prompt (UserPromptSubmit), khi cần approval (PermissionRequest), khi turn dừng (Stop). Hook nhận event data qua stdin JSON, có thể block tool execution bằng exit code 2 hoặc trả JSON {"decision":"block"}.
Bài này deep dive vào 6 hook event chính thức + 5 pattern production từ OpenAI Codex hooks docs (truy cập 20/05/2026), kèm script Python ready-to-paste cho audit log, security block, cost tracking, format-on-save, telemetry. Test trên ongboit production 1 tháng.
- Hook config: khai báo trong
~/.codex/config.tomldưới[[hooks.EventName]]+[[hooks.EventName.hooks]]. - 8 lifecycle event:
SessionStart,PreToolUse,PermissionRequest,PostToolUse,UserPromptSubmit,Stop,PreCompact+PostCompact(2 cuối ship v0.129 30/04/2026, hoàn thiện hook surface). - Matcher regex: field
matcherfilter event theo tool name hoặc source, ví dụmatcher = "^Bash$"chỉ apply cho Bash tool call. - Block execution: hook script exit code 2 + reason ở stderr, hoặc trả JSON
{"decision":"block","reason":"Policy violation"}. - Event data: stdin JSON gồm
session_id,cwd,tool_name,tool_input,tool_response,hook_event_name.
Codex CLI Hooks Là Gì? Lifecycle Event Hoạt Động Ra Sao?
Hook trong Codex CLI là script (thường là Python hoặc shell) tự động chạy tại các điểm lifecycle xác định trong session. Khác với MCP server (mở rộng khả năng tool), hook intercept event để audit, modify, hoặc block hành vi của Codex. Tương đương “git hooks” cho git, hoặc “middleware” cho web framework.
Workflow điển hình: user gửi prompt → UserPromptSubmit hook fire → Codex parse + chọn tool → PreToolUse hook fire (có thể block hoặc modify) → tool execute → PostToolUse hook fire (có thể log result) → Codex respond → Stop hook fire khi turn xong. Mỗi event nhận data JSON qua stdin, trả response qua stdout/stderr.
Use case phổ biến: audit log mọi shell command, block command nguy hiểm (rm -rf /, sudo), track cost token mỗi turn, auto-format file sau edit, push telemetry vào observability stack. Hook khai báo trong ~/.codex/config.toml, detail config syntax xem bài Codex CLI config.toml deep dive. Reader chưa cài Codex CLI? Xem hướng dẫn install Codex CLI 3 OS trước. So sánh hook Codex vs Claude Code chi tiết trong vs-codex verdict 2026.
8 Hook Event Có Sẵn Trong Codex CLI Là Gì?
Per official docs + community repo codex-cli-hooks, Codex CLI hỗ trợ 8 lifecycle event sau khi v0.129.0 (ship 30/04/2026) thêm PreCompact + PostCompact hoàn thiện hook surface:
| Event | Khi nào fire | Use case chính | Có block được? |
|---|---|---|---|
SessionStart |
Lúc session khởi động hoặc resume | Init telemetry, load context, cleanup zombie process | Không |
UserPromptSubmit |
User gửi prompt mới | Log query, intent classification, censor input | Có (block prompt) |
PreToolUse |
Trước khi tool call execute | Audit, security check, modify tool input | Có (block tool) |
PermissionRequest |
Khi tool cần approval | Auto-approve theo policy, log permission ask | Có (deny) |
PostToolUse |
Sau khi tool execute xong | Log result, format-on-save, push metric | Không (chỉ side-effect) |
Stop |
Khi turn conversation kết thúc | Cost summary, save context, alert team | Không |
PreCompact |
Trước khi Codex auto-compact context (token threshold) | Backup context, save important state trước compact, log compact event | Có (skip compact) |
PostCompact |
Sau khi Codex compact context xong | Reload critical context bị drop, restore tool state, alert nếu lost data | Không (chỉ side-effect) |
Mỗi event nhận stdin JSON với fields chung: session_id, cwd, transcript_path, hook_event_name, model. Event-specific fields tuỳ loại: PreToolUse có tool_name + tool_input, PostToolUse có thêm tool_response, UserPromptSubmit có prompt.
Block flow áp dụng cho 4 event: PreToolUse, PermissionRequest, UserPromptSubmit, PreCompact. PostToolUse, PostCompact, Stop chạy sau khi action đã xong, chỉ có side-effect (log, notification), không cancel được. SessionStart cũng chỉ side-effect (Codex đã khởi động rồi). 2 hook compact mới (v0.129) đặc biệt useful cho long-running task qua /goal Ralph Loop, giúp backup state trước compact và restore sau, tránh mất context trong session 6-14 giờ.

Setup Hook Đầu Tiên (Audit Log) Trong 5 Phút Như Thế Nào?
Hook đơn giản nhất là PreToolUse audit log, log mọi tool call vào file. Setup 3 bước:
Bước 1: Tạo thư mục hooks và file script:
mkdir -p ~/.codex/hooks
nano ~/.codex/hooks/audit_log.py
Bước 2: Paste script Python sau:
#!/usr/bin/env python3
# ~/.codex/hooks/audit_log.py
import sys, json, datetime
from pathlib import Path
# Read stdin event data
event = json.loads(sys.stdin.read())
# Format log line
log_line = (
f"{datetime.datetime.now().isoformat()} "
f"session={event.get('session_id', '-')[:8]} "
f"event={event.get('hook_event_name', '-')} "
f"tool={event.get('tool_name', '-')} "
f"cwd={event.get('cwd', '-')}\n"
)
# Append to log file
log_path = Path.home() / ".codex" / "audit.log"
with open(log_path, "a") as f:
f.write(log_line)
# Exit 0 = allow continue, exit 2 = block
sys.exit(0)
Bước 3: Make executable + thêm hook config vào ~/.codex/config.toml:
# Make executable
chmod +x ~/.codex/hooks/audit_log.py
# Thêm vào ~/.codex/config.toml
cat >> ~/.codex/config.toml << 'EOF'
[[hooks.PreToolUse]]
matcher = ".*"
[[hooks.PreToolUse.hooks]]
type = "command"
command = "/usr/bin/env python3 /Users/your-name/.codex/hooks/audit_log.py"
timeout = 30
statusMessage = "Audit log"
EOF
Restart Codex session, gọi vài tool, sau đó check file log: tail -20 ~/.codex/audit.log. Bạn sẽ thấy mỗi tool call có 1 dòng với timestamp + tool name + session ID. Audit trail production-grade trong 5 phút.
5 Pattern Hook Production Đáng Cài Cho Dev 2026 Là Gì?
Sau 1 tháng test trên ongboit, đây là 5 pattern hook ROI cao nhất:
Pattern 1: Security block command nguy hiểm. Block rm -rf, sudo, chmod 777 qua PreToolUse hook trả JSON decision:
# ~/.codex/hooks/security_block.py
import sys, json, re
event = json.loads(sys.stdin.read())
tool_input = event.get("tool_input", {})
command = tool_input.get("command", "") if isinstance(tool_input, dict) else ""
DANGEROUS = [
r"rm\s+-rf\s+/",
r"chmod\s+777",
r"sudo\s+rm",
r">\s*/dev/sd[a-z]", # disk overwrite
r"dd\s+if=.*of=/dev/",
]
for pat in DANGEROUS:
if re.search(pat, command, re.IGNORECASE):
print(json.dumps({
"decision": "block",
"reason": f"Security policy blocks pattern: {pat}"
}))
sys.exit(0)
sys.exit(0)
Config: matcher = "^Bash$" chỉ apply cho Bash tool, không cản tool khác.
Pattern 2: Cost tracking per turn. Stop hook log token usage mỗi turn xong:
# ~/.codex/hooks/cost_tracker.py
import sys, json, datetime
from pathlib import Path
event = json.loads(sys.stdin.read())
session_id = event.get("session_id", "-")[:8]
# Codex stores token usage in transcript file
transcript_path = event.get("transcript_path")
if transcript_path and Path(transcript_path).exists():
# Parse transcript for token counts (simplified)
# In production: use codex CLI stats command
log = Path.home() / ".codex" / "cost.log"
with open(log, "a") as f:
f.write(f"{datetime.datetime.now().isoformat()} "
f"session={session_id} turn-end transcript={transcript_path}\n")
sys.exit(0)
Bind vào event Stop. Cuối ngày run awk hoặc Python script tổng hợp cost từ cost.log.
Pattern 3: Auto-format file sau edit. PostToolUse hook chạy formatter (prettier, black, gofmt) sau khi tool sửa file:
# ~/.codex/hooks/auto_format.py
import sys, json, subprocess
from pathlib import Path
event = json.loads(sys.stdin.read())
tool_input = event.get("tool_input", {})
file_path = tool_input.get("path") if isinstance(tool_input, dict) else None
if not file_path:
sys.exit(0)
ext = Path(file_path).suffix
FORMATTERS = {
".py": ["black", "--quiet"],
".js": ["prettier", "--write"],
".ts": ["prettier", "--write"],
".go": ["gofmt", "-w"],
".rs": ["rustfmt"],
}
if ext in FORMATTERS:
try:
subprocess.run(FORMATTERS[ext] + [file_path], timeout=10, check=False)
except Exception:
pass
sys.exit(0)
Bind vào event PostToolUse với matcher = "^(apply_patch|edit_file)$".
Pattern 4: Session init telemetry. SessionStart hook gửi event tới observability stack (Datadog, New Relic, ongboit dùng n8n webhook):
# ~/.codex/hooks/session_telemetry.py
import sys, json, urllib.request, os
event = json.loads(sys.stdin.read())
payload = {
"session_id": event.get("session_id"),
"cwd": event.get("cwd"),
"model": event.get("model"),
"started_at": event.get("started_at"),
"user": os.environ.get("USER", "-"),
}
webhook_url = os.environ.get("N8N_TELEMETRY_WEBHOOK")
if webhook_url:
try:
req = urllib.request.Request(
webhook_url,
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
)
urllib.request.urlopen(req, timeout=5)
except Exception:
pass
sys.exit(0)
Pattern 5: Prompt content censor (PII redaction). UserPromptSubmit hook redact PII trước khi gửi tới Codex:
# ~/.codex/hooks/pii_redactor.py
import sys, json, re
event = json.loads(sys.stdin.read())
prompt = event.get("prompt", "")
# Redact common PII patterns
PII_PATTERNS = [
(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", "[EMAIL_REDACTED]"),
(r"\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b", "[CARD_REDACTED]"),
(r"\b(?:\+84|0)\d{9,10}\b", "[VN_PHONE_REDACTED]"), # VN phone
]
redacted = prompt
for pattern, replacement in PII_PATTERNS:
redacted = re.sub(pattern, replacement, redacted)
if redacted != prompt:
print(json.dumps({"decision": "approve", "modified_prompt": redacted}))
sys.exit(0)
Sau 1 tháng chạy 5 hook pattern trên ongboit production, 3 sharp edge load-bearing:
- Hook timeout default 600s quá dài cho production. 600 giây = 10 phút, đủ để hang Codex CLI nếu hook script gặp infinite loop hoặc API ngoài bị stuck. Workaround: luôn set
timeout = 10hoặc 30 giây cho hook synchronous, dùng async job queue (n8n webhook) nếu cần I/O lâu hơn. Hook timeout fail silent, không có warning rõ. - PreToolUse block fire trước approval, có thể bypass user intent. Hook security block command với matcher quá broad (
matcher = ".*") có thể block tool user muốn run sau khi đã approve. Workaround: dùngmatcher = "^Bash$"hoặcmatcher = "^(Bash|apply_patch)$"để limit scope. Test pattern kỹ vớicodex --dry-runtrước khi enable. - Hook fail silent khi script Python crash. Nếu script có syntax error hoặc import fail, Codex log nội bộ nhưng không show user. Tool call vẫn execute như không có hook. Workaround: viết wrapper bash gọi Python với
2>>~/.codex/hooks/error.logđể capture stderr. Test hook standalone bằngecho '{"tool_name":"Bash"}' | python3 ~/.codex/hooks/audit_log.pytrước khi enable trong config.
Quy tắc chung: Bắt đầu với 1-2 hook đơn giản (audit log + format-on-save), test kỹ standalone trước khi enable trong config. Hook chạy mỗi event → mỗi millisecond overhead nhân với tần suất tool call. Hook script > 500ms latency sẽ thấy rõ trong daily workflow. Combo hooks + Skills (reusable instruction) + MCP server (external tool) tạo extension stack production. Cost note: hook chạy local nên không charge token, xem pricing breakdown nếu hook gọi external API.

Hook Codex Khác Hook Claude Code Như Thế Nào? Có Tương Thích?
Cả 2 tool đều support hooks lifecycle với pattern giống nhau (event-driven, stdin JSON, exit code block), nhưng convention config khác:
| Aspect | Claude Code hooks | Codex CLI hooks |
|---|---|---|
| Config format | JSON trong settings.json |
TOML trong config.toml |
| Event names | PreToolUse, PostToolUse, UserPromptSubmit, Stop, Notification | + SessionStart, PermissionRequest (6 events) |
| Matcher syntax | Regex string | Regex string (same) |
| Block mechanism | Exit 2 + stderr / JSON decision | Exit 2 + stderr / JSON decision (same) |
| Event data format | JSON stdin (same schema) | JSON stdin (same schema) |
| Script language | Bất kỳ executable | Bất kỳ executable |
Reuse script: 95% script Python viết cho Claude Code hooks chạy được trên Codex mà không cần sửa. Chỉ cần convert config JSON sang TOML. ongboit có 7 hook chia sẻ giữa 2 tool, save vào folder ~/code/shared-hooks/ rồi reference từ cả ~/.claude/settings.json và ~/.codex/config.toml. Detail compatibility pattern dual-tool xem workflow Claude Code và Codex CLI cùng project.
Debug Hook Execution + Timeout Errors Như Thế Nào?
Hook fail silent là vấn đề phổ biến vì Codex chạy script trong subprocess. 4 technique debug:
1. Test standalone qua pipe: echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls"}}' | python3 ~/.codex/hooks/audit_log.py. Nếu script fail ở đây thì lỗi không phải Codex, mà là syntax script.
2. Verbose log mode: CODEX_LOG=debug codex, search log lines có hook_ hoặc HookExec. Sẽ thấy timing + exit code + stderr của mỗi hook.
3. Capture stderr vào file: wrap command trong shell script:
# ~/.codex/hooks/wrapper.sh
#!/bin/bash
/usr/bin/env python3 "$@" 2>> ~/.codex/hooks/error.log
Update config: command = "/Users/your-name/.codex/hooks/wrapper.sh /Users/your-name/.codex/hooks/audit_log.py".
4. Check timeout: default 600s nhưng nếu set timeout = 10 và script lâu hơn, Codex log timeout error. Tăng dần từ 10 → 30 → 60 cho hook I/O network. Async hook (gửi webhook fire-and-forget) nên dùng subprocess.Popen + return ngay, không block.
Codex CLI MCP Setup: 7 Server + Top 5 Universal 2026 – Đã setup hooks? Bước tiếp theo: MCP server kết nối tool ngoài (GitHub, Context7, Playwright). Hooks + MCP là extension stack production-grade.
Quay lại pillar tổng quan: Codex CLI là gì – hướng dẫn toàn diện.
Câu Hỏi Thường Gặp
Hook script viết bằng ngôn ngữ gì? Có cần Python không?
Không cần Python cụ thể. Hook chỉ cần file executable + đường dẫn đầy đủ trong field command. Bash, Python, Node.js, Go binary, Rust binary đều dùng được. Python phổ biến nhất vì syntax đơn giản cho parse JSON stdin + write log. Bash đơn giản cho audit log thuần. Go/Rust dùng khi cần performance cao (production traffic lớn). Trên ongboit dùng Python cho 5/7 hook, Bash 2 hook đơn giản.
Hook timeout 600s mặc định có quá dài không?
Có, quá dài cho synchronous hook. 600 giây = 10 phút có thể hang Codex CLI nếu script gặp infinite loop hoặc API ngoài bị stuck. Recommend pattern: hook synchronous (audit log, security check) set timeout = 10 giây, hook async (telemetry webhook) viết theo fire-and-forget pattern. Tổng hook overhead nên dưới 100ms để không thấy lag rõ trong daily workflow.
Hook chạy có làm Codex CLI chậm rõ rệt không?
Có nếu hook script chậm. PreToolUse hook chạy SYNCHRONOUS trước mỗi tool call, mỗi 100ms hook overhead nhân với 50 tool call/session = 5 giây lag. Cách giảm overhead: viết hook bằng compiled language (Go, Rust) thay vì Python interpreter cold start, hoặc chỉ enable hook trong profile cụ thể qua [profiles.audit] rồi switch codex --profile audit khi cần. Bài config.toml deep dive có pattern profile switching.
Hook có thể call Codex CLI bên trong (recursive) không?
Technical là được nhưng KHÔNG NÊN. Hook chạy bên trong Codex session sẽ tạo nested process, có thể loop vô hạn nếu hook trigger event khác. Codex docs không khuyến nghị recursive call. Use case “spawn agent từ hook” nên dùng async job queue (n8n trigger) hoặc codex exec trong process tách biệt với nohup &.
Backup hook trước khi thử pattern mới như thế nào?
Đơn giản nhất là version-control toàn bộ ~/.codex/hooks/ qua git private repo. Mỗi pattern hook = 1 commit, dễ rollback khi break. Pattern khác: cp -r ~/.codex/hooks ~/.codex/hooks.bak.$(date +%Y%m%d) trước khi enable hook mới. Rollback: mv ~/.codex/hooks.bak.YYYYMMDD ~/.codex/hooks. Test hook mới trong profile riêng ([profiles.test-hooks]) trước khi promote sang default.
