| name | security |
| description | Production-grade security with authentication (API keys, JWT), authorization (RBAC), rate limiting (token bucket, sliding window), request validation, and middleware integration. Use when securing actors, implementing access control, or preventing abuse. |
TrebuchetSecurity
Production-grade security for distributed actors.
Overview
TrebuchetSecurity provides comprehensive security features for Trebuchet distributed actors:
- Authentication: API keys and JWT tokens
- Authorization: Role-based access control (RBAC)
- Rate Limiting: Token bucket and sliding window algorithms
- Request Validation: Payload size limits and input validation
Quick Start
import TrebuchetCloud
import TrebuchetSecurity
let apiAuth = APIKeyAuthenticator(keys: [
.init(key: "sk_live_abc123", principalId: "service-1", roles: ["service"])
])
let policy = RoleBasedPolicy(rules: [
.adminFullAccess,
.userReadOnly
])
let limiter = TokenBucketLimiter(
requestsPerSecond: 100,
burstSize: 200
)
let validator = RequestValidator(configuration: .strict)
let authMiddleware = AuthenticationMiddleware(provider: apiAuth)
let authzMiddleware = AuthorizationMiddleware(policy: policy)
let rateLimitMiddleware = RateLimitingMiddleware(limiter: limiter)
let validationMiddleware = ValidationMiddleware(validator: validator)
let gateway = CloudGateway(configuration: .init(
middlewares: [
validationMiddleware,
authMiddleware,
authzMiddleware,
rateLimitMiddleware
],
stateStore: stateStore,
registry: registry
))
Authentication
Verify user and service identities.
API Key Authentication
Simple, secure authentication for services:
import TrebuchetSecurity
let apiAuth = APIKeyAuthenticator(keys: [
.init(
key: "sk_live_abc123",
principalId: "service-worker-1",
roles: ["service", "worker"]
),
.init(
key: "sk_admin_xyz789",
principalId: "admin-bot",
roles: ["admin"],
expiresAt: Date().addingTimeInterval(86400)
)
])
let credentials = Credentials.apiKey(key: "sk_live_abc123")
let principal = try await apiAuth.authenticate(credentials)
print(principal.id)
print(principal.roles)
Dynamic Key Management
let apiAuth = APIKeyAuthenticator()
await apiAuth.register(.init(
key: "sk_new_key",
principalId: "new-service",
roles: ["service"]
))
await apiAuth.revoke("sk_old_key")
JWT Authentication
Security Warning: The included JWT implementation does NOT validate signatures and is for testing only.
For production, use a proper JWT library:
let jwtAuth = JWTAuthenticator(configuration: .init(
issuer: "https://auth.example.com",
audience: "https://api.example.com",
clockSkew: 60
))
let credentials = Credentials.bearer(token: jwtToken)
let principal = try await jwtAuth.authenticate(credentials)
Migration Note (v0.3.0): If using the built-in JWT implementation (not recommended for production):
SigningKey.symmetric(secret:) → Use SigningKey.hs256(secret:) instead
SigningKey.asymmetric(publicKey:) → Use SigningKey.es256(publicKey:) instead
For production JWT authentication, integrate a proper JWT library with TrebuchetSecurity's AuthenticationProvider protocol.
Principal
The Principal struct represents an authenticated identity:
public struct Principal: Sendable, Codable {
public let id: String
public let type: PrincipalType
public let roles: Set<String>
public let attributes: [String: String]
public let authenticatedAt: Date
public let expiresAt: Date?
}
if principal.hasRole("admin") {
}
if principal.hasAnyRole(["admin", "moderator"]) {
}
Authorization
Control access to actors and methods with RBAC.
Basic RBAC
import TrebuchetSecurity
let policy = RoleBasedPolicy(rules: [
.init(role: "admin", actorType: "*", method: "*"),
.init(role: "user", actorType: "*", method: "get*"),
.init(role: "worker", actorType: "GameRoom", method: "*")
])
let action = Action(actorType: "GameRoom", method: "join")
let resource = Resource(type: "game", id: "room-123")
let allowed = try await policy.authorize(
principal,
action: action,
resource: resource
)
if !allowed {
throw AuthorizationError.accessDenied
}
Pattern Matching
Rules support wildcard patterns:
.init(role: "user", actorType: "*", method: "get*")
.init(role: "monitor", actorType: "*", method: "*Status")
.init(role: "admin", actorType: "AdminPanel", method: "reset")
.init(role: "admin", actorType: "*", method: "*")
Predefined Rules
let policy = RoleBasedPolicy(rules: [
.adminFullAccess,
.userReadOnly,
.serviceInvoke
])
Multi-Role Example
let gamePolicy = RoleBasedPolicy(rules: [
.init(role: "admin", actorType: "*", method: "*"),
.init(role: "player", actorType: "GameRoom", method: "join"),
.init(role: "player", actorType: "GameRoom", method: "leave"),
.init(role: "premium", actorType: "GameRoom", method: "create"),
.init(role: "moderator", actorType: "GameRoom", method: "kick"),
.init(role: "user", actorType: "Lobby", method: "get*")
])
Rate Limiting
Protect actors from abuse.
Token Bucket Limiter
Allow controlled bursts while maintaining average rate:
import TrebuchetSecurity
let limiter = TokenBucketLimiter(
requestsPerSecond: 100,
burstSize: 200
)
let result = try await limiter.checkLimit(key: principal.id)
if !result.allowed {
throw RateLimitError.limitExceeded(retryAfter: result.retryAfter!)
}
Sliding Window Limiter
Precise per-window limits with smooth transitions:
let limiter = SlidingWindowLimiter(
maxRequests: 1000,
windowSize: .seconds(60)
)
let result = try await limiter.checkLimit(key: principal.id)
Rate Limit Result
public struct RateLimitResult {
let allowed: Bool
let remaining: Int
let retryAfter: Duration?
}
Request Validation
Validate incoming requests.
Configuration
let validator = RequestValidator(configuration: .init(
maxPayloadSize: 1024 * 1024,
maxActorIDLength: 256,
maxMethodNameLength: 128,
allowedMethodNamePattern: "[a-zA-Z][a-zA-Z0-9_]*"
))
try await validator.validate(invocation)
Preset Configurations
let validator = RequestValidator(configuration: .strict)
let validator = RequestValidator(configuration: .lenient)
Middleware Integration
Security components integrate via middleware:
import TrebuchetCloud
import TrebuchetSecurity
let middlewares: [CloudMiddleware] = [
ValidationMiddleware(validator: validator),
AuthenticationMiddleware(provider: apiAuth),
AuthorizationMiddleware(policy: policy),
RateLimitingMiddleware(limiter: limiter)
]
let gateway = CloudGateway(configuration: .init(
middlewares: middlewares,
stateStore: stateStore,
registry: registry
))
Security Flow
1. Request arrives
↓
2. ValidationMiddleware validates payload
↓
3. AuthenticationMiddleware verifies credentials → Principal
↓
4. AuthorizationMiddleware checks permissions
↓
5. RateLimitingMiddleware checks rate limits
↓
6. Request processed by actor
Security Presets
Development
Permissive settings for local development:
let devAuth = APIKeyAuthenticator(keys: [
.init(key: "dev_test", principalId: "dev", roles: ["admin"])
])
let devPolicy = RoleBasedPolicy(rules: [
.init(role: "admin", actorType: "*", method: "*")
], denyByDefault: false)
let devLimiter = TokenBucketLimiter(
requestsPerSecond: 1000,
burstSize: 2000
)
let devValidator = RequestValidator(configuration: .init(
maxPayloadSize: 10 * 1024 * 1024,
maxActorIDLength: 1000,
maxMethodNameLength: 500
))
Production
Strict settings for production:
let prodAuth = JWTAuthenticator(configuration: .init(
issuer: "https://auth.example.com",
audience: "https://api.example.com"
))
let prodPolicy = RoleBasedPolicy(rules: [
.adminFullAccess,
.userReadOnly
])
let prodLimiter = TokenBucketLimiter(
requestsPerSecond: 10,
burstSize: 20
)
let prodValidator = RequestValidator(configuration: .strict)
Error Handling
do {
try await processSecureRequest()
} catch AuthenticationError.invalidCredentials {
return .unauthorized("Invalid credentials")
} catch AuthenticationError.expired {
return .unauthorized("Credentials expired")
} catch AuthorizationError.accessDenied {
return .forbidden("Access denied")
} catch RateLimitError.limitExceeded(let retryAfter) {
return .tooManyRequests(retryAfter: retryAfter)
} catch ValidationError.payloadTooLarge(let size, let limit) {
return .requestTooLarge("Payload \(size) exceeds limit \(limit)")
}
Best Practices
Defense in Depth
Use multiple security layers:
let middleware = [
ValidationMiddleware(...),
AuthenticationMiddleware(...),
AuthorizationMiddleware(...),
RateLimitingMiddleware(...)
]
Least Privilege
Grant minimum necessary permissions:
.init(role: "user", actorType: "GameRoom", method: "join")
.init(role: "user", actorType: "*", method: "*")
Audit Logging
Log all security decisions:
logger.info("Authorization decision", metadata: [
"principal": principal.id,
"action": "\(action.actorType).\(action.method)",
"resource": resource.id,
"allowed": "\(allowed)"
])
Secure Defaults
Start strict, relax as needed:
let validator = RequestValidator(configuration: .strict)
let validator = RequestValidator(configuration: .lenient)
Custom Providers
Implement custom authentication:
actor CustomAuthenticator: AuthenticationProvider {
func authenticate(_ credentials: Credentials) async throws -> Principal {
guard case .custom(let type, let value) = credentials,
type == "device-token" else {
throw AuthenticationError.malformed(reason: "Expected device token")
}
let deviceInfo = try await validateDeviceToken(value)
return Principal(
id: deviceInfo.deviceId,
type: .system,
roles: ["device", "iot"]
)
}
}
Implement custom authorization:
actor TimeBasedPolicy: AuthorizationPolicy {
let allowedHours: ClosedRange<Int>
func authorize(
_ principal: Principal,
action: Action,
resource: Resource
) async throws -> Bool {
let hour = Calendar.current.component(.hour, from: Date())
guard allowedHours.contains(hour) else {
return false
}
return principal.hasRole("admin") || principal.hasRole("user")
}
}
See Also
- Cloud deployment guide for CloudGateway integration
- Observability guide for security event logging
- AWS Lambda guide for production deployment