Airlock Decorator¶
The main entry point for Agent-Airlock.
Airlock¶
Signature¶
def Airlock(
config: AirlockConfig | None = None,
policy: SecurityPolicy | Callable[[AirlockContext], SecurityPolicy] | None = None,
sandbox: bool = False,
sandbox_required: bool = False,
agent_id: str | None = None,
# V0.4.0 additions
unknown_args_mode: UnknownArgsMode | None = None,
capability_policy: CapabilityPolicy | None = None,
circuit_breaker: CircuitBreaker | None = None,
cost_tracker: CostTracker | None = None,
retry_policy: RetryPolicy | None = None,
return_dict: bool = False,
) -> Callable[[F], F]:
"""
Decorator that wraps a function with security validation.
Args:
config: Configuration options (sanitization, output limits, etc.)
policy: Security policy (RBAC, rate limits, time restrictions)
Can be a callable for dynamic resolution.
sandbox: If True, execute in E2B sandbox
sandbox_required: If True, fail if sandbox unavailable
agent_id: Identifier for the calling agent
unknown_args_mode: How to handle unknown arguments (V0.4.0)
capability_policy: Fine-grained capability gating (V0.4.0)
circuit_breaker: Fault tolerance configuration (V0.4.0)
cost_tracker: Cost monitoring and budget limits (V0.4.0)
retry_policy: Automatic retry with backoff (V0.4.0)
return_dict: If True, always return dict instead of AirlockResponse
Returns:
Decorated function with security wrapper
"""
Basic Usage¶
With UnknownArgsMode (V0.4.0)¶
from agent_airlock import Airlock, UnknownArgsMode
# Production - reject unknown arguments
@Airlock(unknown_args_mode=UnknownArgsMode.BLOCK)
def prod_tool(x: int) -> int:
return x * 2
# Staging - strip and log
@Airlock(unknown_args_mode=UnknownArgsMode.STRIP_AND_LOG)
def staging_tool(x: int) -> int:
return x * 2
# Development - silently strip
@Airlock(unknown_args_mode=UnknownArgsMode.STRIP_SILENT)
def dev_tool(x: int) -> int:
return x * 2
With Configuration¶
from agent_airlock import Airlock, AirlockConfig
config = AirlockConfig(
sanitize_output=True,
mask_pii=True,
)
@Airlock(config=config)
def my_tool(x: int) -> int:
return x * 2
With Policy¶
from agent_airlock import Airlock, SecurityPolicy
policy = SecurityPolicy(
rate_limits={"*": "100/hour"},
)
@Airlock(policy=policy)
def my_tool(x: int) -> int:
return x * 2
With Dynamic Policy (V0.1.5+)¶
from agent_airlock import Airlock, SecurityPolicy, AirlockContext
def resolve_policy(ctx: AirlockContext) -> SecurityPolicy:
"""Resolve policy based on context."""
if ctx.workspace_id == "enterprise":
return SecurityPolicy(rate_limits={"*": "10000/hour"})
return SecurityPolicy(rate_limits={"*": "100/hour"})
@Airlock(policy=resolve_policy)
def my_tool(x: int) -> int:
return x * 2
With Sandbox¶
With Capability Gating (V0.4.0)¶
from agent_airlock import Airlock, Capability, requires
@Airlock()
@requires(Capability.FILESYSTEM_READ)
def read_tool(path: str) -> str:
return open(path).read()
@Airlock()
@requires(Capability.FILESYSTEM_READ | Capability.NETWORK_HTTP)
def fetch_and_save(url: str, path: str) -> bool:
data = requests.get(url).text
open(path, "w").write(data)
return True
With Circuit Breaker (V0.4.0)¶
from agent_airlock import Airlock, AGGRESSIVE_BREAKER
@Airlock(circuit_breaker=AGGRESSIVE_BREAKER)
def external_api_call(query: str) -> dict:
return requests.get(f"https://api.example.com?q={query}").json()
With Cost Tracking (V0.4.0)¶
from agent_airlock import Airlock, CostTracker, BudgetConfig
tracker = CostTracker(budget=BudgetConfig(hard_limit=100.0))
@Airlock(cost_tracker=tracker)
def expensive_tool(query: str) -> str:
return call_expensive_api(query)
With Retry Policy (V0.4.0)¶
from agent_airlock import Airlock, STANDARD_RETRY
@Airlock(retry_policy=STANDARD_RETRY)
def flaky_tool(query: str) -> dict:
return requests.get(f"https://flaky-api.com?q={query}").json()
Async Support¶
Streaming Support (V0.1.5+)¶
from agent_airlock import StreamingAirlock
@StreamingAirlock()
def stream_tool(query: str):
for chunk in generate_chunks(query):
yield chunk
@StreamingAirlock()
async def async_stream_tool(query: str):
async for chunk in generate_async_chunks(query):
yield chunk
AirlockResponse¶
Response object for blocked calls.
Attributes¶
| Attribute | Type | Description |
|---|---|---|
status |
str |
"blocked" or "success" |
error |
str \| None |
Error message |
fix_hints |
list[str] |
Corrective suggestions for LLM |
blocked_args |
list[str] |
Arguments that were rejected |
tool_name |
str |
Name of the tool |
validation_errors |
list[dict] |
Detailed validation errors |
Example¶
from agent_airlock import Airlock, UnknownArgsMode
@Airlock(unknown_args_mode=UnknownArgsMode.BLOCK)
def my_tool(x: int) -> int:
return x * 2
result = my_tool(x="invalid", ghost=True)
# result is AirlockResponse:
# {
# "status": "blocked",
# "error": "Validation failed",
# "fix_hints": [
# "x: Expected int, got str. Try: x=0",
# "Remove unknown parameters: ghost"
# ],
# "blocked_args": ["ghost"],
# "tool_name": "my_tool"
# }
Safe Types (V0.4.0)¶
SafePath¶
from agent_airlock import SafePath, SafePathStrict, SafePathInTmp
def read_file(path: SafePath) -> str:
"""Path validated against traversal attacks."""
return open(path).read()
def write_temp(path: SafePathInTmp) -> bool:
"""Path must be in /tmp."""
...
SafeURL¶
from agent_airlock import SafeURL, SafeURLAllowHttp
def fetch_api(url: SafeURL) -> dict:
"""URL must be HTTPS."""
return requests.get(url).json()
def fetch_legacy(url: SafeURLAllowHttp) -> dict:
"""URL can be HTTP or HTTPS."""
return requests.get(url).json()
Capability (V0.4.0)¶
from agent_airlock import Capability
# Available capabilities (Flag enum, can combine with |)
Capability.FILESYSTEM_READ
Capability.FILESYSTEM_WRITE
Capability.NETWORK_HTTP
Capability.NETWORK_SOCKET
Capability.PROCESS_SPAWN
Capability.DATABASE_READ
Capability.DATABASE_WRITE
CircuitBreaker (V0.4.0)¶
from agent_airlock import CircuitBreaker, CircuitState
breaker = CircuitBreaker(...)
# Check state
breaker.state # CircuitState.CLOSED, OPEN, or HALF_OPEN
# Get stats
stats = breaker.stats # CircuitStats with failure_count, success_count, etc.
Predefined Constants¶
Policies¶
from agent_airlock import (
PERMISSIVE_POLICY,
STRICT_POLICY,
READ_ONLY_POLICY,
BUSINESS_HOURS_POLICY,
)
Capability Policies (V0.4.0)¶
from agent_airlock import (
PERMISSIVE_CAPABILITY_POLICY,
STRICT_CAPABILITY_POLICY,
READ_ONLY_CAPABILITY_POLICY,
NO_NETWORK_CAPABILITY_POLICY,
)
Circuit Breakers (V0.4.0)¶
Retry Policies (V0.4.0)¶
from agent_airlock import (
NO_RETRY,
FAST_RETRY,
STANDARD_RETRY,
AGGRESSIVE_RETRY,
PATIENT_RETRY,
)
Unknown Args Modes (V0.4.0)¶
from agent_airlock import (
PRODUCTION_MODE, # UnknownArgsMode.BLOCK
STAGING_MODE, # UnknownArgsMode.STRIP_AND_LOG
DEVELOPMENT_MODE, # UnknownArgsMode.STRIP_SILENT
)
Utility Functions¶
get_current_context¶
Get the current Airlock context (available inside tools):
from agent_airlock import get_current_context
@Airlock()
def my_tool(x: int) -> int:
ctx = get_current_context()
print(f"Workspace: {ctx.workspace_id}")
return x * 2
observe (V0.4.0)¶
Context manager/decorator for observability:
from agent_airlock import observe
@observe("my_operation")
def my_function():
...
with observe("my_operation", tool_name="my_tool") as span:
span.set_attribute("key", "value")
...