Blueprints¶
Blueprints let you organize handlers into reusable, composable modules.
Why Blueprints?¶
As your hooks grow, a single file becomes hard to manage:
# hooks.py - getting messy!
@app.pre_tool("Bash")
def security_check(event): ...
@app.pre_tool("Write")
def security_write(event): ...
@app.pre_tool("Bash")
def logging_bash(event): ...
@app.post_tool("Write")
def logging_write(event): ...
@app.on_stop()
def cleanup(event): ...
Blueprints let you split by concern:
hooks/
├── __init__.py # Main app
├── security.py # Security rules
├── logging.py # Audit logging
└── cleanup.py # Session cleanup
Basic Usage¶
# security.py
from fasthooks import Blueprint, deny
security = Blueprint("security")
@security.pre_tool("Bash")
def no_dangerous_commands(event):
if "rm -rf" in event.command:
return deny("Dangerous command blocked")
@security.pre_tool("Write")
def protect_sensitive_files(event):
if ".env" in event.file_path:
return deny("Cannot modify .env files")
# hooks.py
from fasthooks import HookApp
from security import security
app = HookApp()
app.include(security)
if __name__ == "__main__":
app.run()
Blueprint API¶
Blueprints support the same decorators as HookApp:
from fasthooks import Blueprint
bp = Blueprint("my-blueprint")
# Tool events
@bp.pre_tool("Bash")
def check(event): ...
@bp.post_tool("Write")
def after(event): ...
@bp.on_permission("Bash")
def perm(event): ...
# Lifecycle events
@bp.on_stop()
def stop(event): ...
@bp.on_session_start()
def start(event): ...
Real-World Examples¶
Security Blueprint¶
# blueprints/security.py
from fasthooks import Blueprint, deny
security = Blueprint("security")
DANGEROUS_PATTERNS = [
"rm -rf",
"mkfs",
"> /dev/sd",
"dd if=",
]
PROTECTED_PATHS = [
".env",
".ssh",
"credentials",
"secrets",
]
@security.pre_tool("Bash")
def block_dangerous_commands(event):
for pattern in DANGEROUS_PATTERNS:
if pattern in event.command:
return deny(f"Blocked: contains '{pattern}'")
@security.pre_tool("Write")
def protect_files(event):
for path in PROTECTED_PATHS:
if path in event.file_path:
return deny(f"Cannot write to {path}")
@security.pre_tool("Edit")
def protect_edits(event):
for path in PROTECTED_PATHS:
if path in event.file_path:
return deny(f"Cannot edit {path}")
Logging Blueprint¶
# blueprints/logging.py
from fasthooks import Blueprint
from fasthooks.depends import State
from datetime import datetime
logging = Blueprint("logging")
@logging.post_tool("Bash")
def log_bash(event, state: State):
logs = state.get("bash_log", [])
logs.append({
"command": event.command,
"time": datetime.now().isoformat(),
})
state["bash_log"] = logs[-100:] # Keep last 100
state.save()
@logging.post_tool("Write")
def log_write(event, state: State):
logs = state.get("write_log", [])
logs.append({
"file": event.file_path,
"time": datetime.now().isoformat(),
})
state["write_log"] = logs[-100:]
state.save()
Rate Limiting Blueprint¶
# blueprints/rate_limit.py
from fasthooks import Blueprint, deny
from fasthooks.depends import State
rate_limit = Blueprint("rate-limit")
LIMITS = {
"Bash": 100,
"Write": 50,
"Edit": 50,
}
@rate_limit.pre_tool("*")
def check_rate(event, state: State):
tool = event.tool_name
if tool not in LIMITS:
return
key = f"rate_{tool}"
count = state.get(key, 0) + 1
state[key] = count
state.save()
limit = LIMITS[tool]
if count > limit:
return deny(f"Rate limit exceeded: {count}/{limit} {tool} calls")
Composing Multiple Blueprints¶
# hooks.py
from fasthooks import HookApp
from blueprints.security import security
from blueprints.logging import logging
from blueprints.rate_limit import rate_limit
app = HookApp(state_dir="/tmp/fasthooks-state")
# Include all blueprints
app.include(security)
app.include(logging)
app.include(rate_limit)
# Add app-specific handlers
@app.on_stop()
def final_check(event):
...
if __name__ == "__main__":
app.run()
When to Use Blueprints¶
| Scenario | Blueprint? |
|---|---|
| Single file with <10 handlers | No |
| Handlers grouped by concern (security, logging) | Yes |
| Reusable rules across projects | Yes |
| Team members working on different features | Yes |
| Conditionally enabling feature sets | Yes |
Blueprint vs Middleware¶
- Blueprints: Organize handlers by feature/concern
- Middleware: Cross-cutting logic that wraps ALL handlers
Use blueprints when you want modular organization. Use middleware when you want universal behavior (timing, logging every call).