Security Integration
Dual-Model Security Architecture
ContextUnity enforces a strict “Token as Single Point of Truth (SPOT)” security model based on Per-Project Signing & Token Isolation.
There are two operation modes, automatically detected at startup:
1. Open Source Mode (HMAC Keystream)
Used when deploying independent projects without the centralized Enterprise Shield.
- Config:
CU_PROJECT_SECRET(unique per project). - Backend:
HmacBackend(stdlib HMAC-SHA256). - Flow: The project’s ContextUnity SDK hashes its manifest against
CU_PROJECT_SECRETto mint an ephemeral registration token. The Router verifies this using its ownCU_PROJECT_SECRETmapping from Redis.
2. Enterprise Mode (Shield Session Tokens)
Used in large, multi-tenant deployments managed by contextunity.shield.
- Config:
CU_SHIELD_GRPC_URLis set. - Backend:
SessionTokenBackend(Ed25519). - Flow: Projects establish a zero-trust binding to Shield. A project’s tools and permissions are registered out-of-band by an Infrastructure Operator using the Admin CLI (
uv run python -m contextunity.core.cli.admin sync-policy manifest.yaml). At runtime, Shield issues a short-lived Session Token signed with its private Ed25519 node key. All services verify the token instantly using Shield’s public key, without database lookups. Permissions are strictly controlled by the Shield Policy Engine, not self-declared by the project.
There is no UnsignedBackend or SECURITY_ENFORCEMENT toggle. Security is always enforced.
Universal Capability Stripping (Manifest-Driven ABAC)
ContextUnity implements zero-trust execution boundaries at the node level via Universal Capability Stripping. When a LangGraph node or tool is executed by contextunity.router:
- Manifest Parsing: The router reads the node’s declarative
contextunity.project.yamldefinition. - Scope Derivation: It calculates the absolute minimum permissions needed (e.g.,
shield:secrets:read:<tenant>/api_keys/MY_MODEL_KEY,zero:anonymize,tool:my_tool). - Token Attenuation: The router invokes
TokenBuilder().attenuate()right before calling the node’s function, stripping all other privileges from the in-memory ContextToken and appending the node’s name (>node:model_executor) to the cryptographically signedprovenancechain.
This guarantees that even if an execution node is compromised via prompt injection, it cannot read other project secrets via contextunity.shield or execute unauthorized tools, enforcing a true Least-Privilege Architecture inside the router.
Derived Permissions
Under the Manifest-First architecture, permissions are Derived Permissions.
If a project’s contextunity.project.yaml declares that it uses a specific LLM and a specific federated tool, the SDK’s ProjectBootstrapConfig and the Router’s registration compilation automatically compute the required permission scopes and grant them to the ContextToken. You do not need to manually configure or document these low-level string grants unless you are developing a new core platform service.
Token Handling & Interceptors
Server-side: SecurityGuard + ServicePermissionInterceptor
Each service constructs its own ServicePermissionInterceptor with a service-specific RPC_PERMISSION_MAP. See contextunity.router.service.interceptors or contextunity.brain.service.interceptors for examples.
import grpcfrom contextunity.core import ( ServicePermissionInterceptor, # Server-side: checks per-RPC permissions get_security_guard, # Token validation, Shield firewall TokenMetadataInterceptor, # Client-side: injects token into metadata)
# In handler: validate token and Shield checksguard = get_security_guard()async def MyMethod(self, request, context): token = guard.validate_token(context) result = await guard.check_input(user_input) if result.blocked: context.abort(grpc.StatusCode.PERMISSION_DENIED, result.reason)Client-side: TokenMetadataInterceptor
from contextunity.core import TokenMetadataInterceptor
interceptor = TokenMetadataInterceptor(token)channel = grpc.aio.insecure_channel("brain:50051", interceptors=[interceptor])Auth Backends
The SDK uses AuthBackend for token attachment. Verification happens service-side.
from contextunity.core import ( AuthBackend, # Protocol HmacBackend, # Open Source: HMAC-SHA256 SessionTokenBackend, # Enterprise: Ed25519 signed by Shield get_signing_backend, set_signing_backend,)
# Backend is set at bootstrap (bootstrap_django / bootstrap_standalone)backend = get_signing_backend()metadata = backend.create_grpc_metadata(token) # For gRPC calls| Backend | Mode | Config Trigger |
|---|---|---|
HmacBackend | Open Source | Shield disabled + CU_PROJECT_SECRET |
SessionTokenBackend | Enterprise | services.shield.enabled in manifest + CU_SHIELD_GRPC_URL |
Redis Integrity Protection (Encrypt-then-MAC)
ContextUnity uses a centralized Redis registry (discovery.py) so all services can verify incoming gRPC requests. For Open Source projects, this registry stores their HMAC project_secret. Because Redis might be hosted externally, these secrets are never stored in plaintext.
Required Configuration for all ContextUnity platform services (Router, Brain, Worker, View, etc.):
REDIS_SECRET_KEY=yoursecret32bytes...When REDIS_SECRET_KEY is configured:
- The Router (during registration) uses Encrypt-then-MAC (HMAC counter-mode keystream + SHA256 MAC) to encrypt HMAC strings before saving them.
- All other services (Brain, Worker, etc.) use this exact same
REDIS_SECRET_KEYto decrypt the HMAC secret when they intercept an incoming gRPC request, allowing them to verify the token signature.
Note on Enterprise Shield mode: For projects using contextunity.shield, the Router asks Shield for the
public_key(Ed25519) and stores it in Redis in plaintext JSON. Because these are public keys, the_decryptfunction safely bypasses them. However,REDIS_SECRET_KEYmust still be configured identically across the entire service cluster so that the platform can simultaneously support both Enterprise and HMAC projects in a Hybrid mode. For local development, setREDIS_SECRET_KEY=false.
Migrating from Open-Source (HMAC) to Enterprise (Shield)
To upgrade a project to the contextunity.shield zero-trust architecture:
- Deploy the contextunity.shield service and point
CU_SHIELD_GRPC_URLin the Router/Brain/Worker templates to it. - Create the project in Shield. This generates the Ed25519 keypair and an admin token:
Terminal window contextshield project-create my-project \--permissions "brain:read,brain:write,router:execute"# Output includes admin_token — save it to Ansible vault - Use the admin token to manage project permissions remotely (no
SHIELD_MASTER_KEYneeded):Terminal window contextshield project-policy my-project \--admin-token "shield-admin:my-project:Token..." \--permissions "brain:read,brain:write,router:execute" - Set
CU_SHIELD_GRPC_URLin the project’s own.env.
As soon as the SDK detects CU_SHIELD_GRPC_URL, it will automatically switch to the SessionTokenBackend handshake flow.
Service Discovery & Project Registry
To prevent tenant spoofing (“Tenant B claiming to be Project A’s owner”), ContextUnity uses a centralized Redis registry.
- Service Verification (Anti-Spoofing): Enforces that the incoming
ContextToken.tenant_idlegally owns theproject_id. - Service Discovery: Allows gRPC nodes to find each other dynamically without hardcoded IPs.
For the full implementation guide, fallback mechanisms, and Redis configuration, see Project Registry & Discovery.
gRPC TLS
from contextunity.core.grpc_utils import create_channel_sync, create_channel
# TLS is automatically enabled if GRPC_TLS_ENABLED=true or the URL starts with grpcs://channel = create_channel_sync("brain.local:50051")