Development Guide¶
Prerequisites¶
- Docker + Docker Compose
- Python 3.11+ with
pipenv - Node 20+ with
npm - PostgreSQL 15+ (or use Docker)
- Redis 7+ (or use Docker)
Quick Start (Docker)¶
git clone https://github.com/calliopeai/zentinelle
cd zentinelle
cp .env.example .env
docker compose up
Services available: - GRC Portal: http://localhost:3002 - API: http://localhost:8000/api/zentinelle/ - GraphQL: http://localhost:8000/gql/zentinelle/
Local Dev (No Docker)¶
Backend¶
cd backend
pipenv install
cp ../.env.example .env # or set env vars
pipenv run python manage.py migrate
pipenv run python manage.py createsuperuser
pipenv run python manage.py runserver # port 8000
In a separate terminal:
cd backend
pipenv run celery -A config worker -l info # event processing
pipenv run celery -A config beat -l info # periodic tasks
Frontend (GRC Portal)¶
Testing¶
cd backend
pipenv run pytest # all tests
pipenv run pytest zentinelle/tests/ # zentinelle only
pipenv run pytest zentinelle/tests/test_policy_engine.py # specific file
pipenv run pytest -x # stop on first failure
pipenv run pytest --cov=zentinelle # with coverage
Pre-Commit Validation¶
Run before every commit:
cd backend
# 1. Django startup check (catches import errors, forward references)
pipenv run python -c "import django, os; os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings'); django.setup()"
# 2. Migration check
pipenv run python manage.py makemigrations --check --dry-run
# 3. Tests
pipenv run pytest
# 4. Lint
pipenv run flake8
pipenv run isort --check-only .
GraphQL Workflow¶
Zentinelle has one GraphQL endpoint: POST /gql/zentinelle/
# 1. Modify schema in backend/zentinelle/schema/
# 2. Export schema file
cd backend && pipenv run python manage.py dev_utils --generate_schema
# 3. Regenerate TypeScript types (frontend must have backend running)
cd frontend && npm run compile
Schema Ordering Rule¶
Critical: All ObjectType and InputType classes MUST be defined before any Query or Mutation class that references them. Forward references crash Django startup.
Correct order in every schema file:
1. Imports
2. Enums and InputTypes
3. ObjectTypes
4. Result types (*Result, *Connection)
5. Query class
6. Mutation class
Migrations¶
cd backend
# Create migration after model changes
pipenv run python manage.py makemigrations zentinelle
# Apply migrations
pipenv run python manage.py migrate
# Check for unapplied migrations
pipenv run python manage.py showmigrations
All Zentinelle models live in the zentinelle schema (via DB router). Migrations are in zentinelle/migrations/.
Adding a New Policy Type¶
- Add the policy type to
zentinelle/models/policy.py(PolicyTypeenum) - Implement the evaluator in
zentinelle/services/policy_engine.py - Add GraphQL types in
zentinelle/schema/types/policy.py - Add mutations in
zentinelle/schema/mutations/policy.py - Update
docs/wiki/policies.mdwith the new policy config format - Write tests in
zentinelle/tests/test_policy_engine.py
Adding a New REST Endpoint¶
- Create view in
zentinelle/api/ - Register URL in
zentinelle/api/urls.py - Add auth middleware (JWT validation via
TenantResolver) - Write tests
- Update
docs/wiki/api.md
Adding an SDK Framework Plugin¶
Plugins live in zentinelle-sdk/plugins/. Each plugin:
1. Wraps the core SDK client
2. Intercepts the framework's LLM calls
3. Calls evaluate() before and emit() after
4. Handles errors gracefully (never break the agent)
See zentinelle-sdk/plugins/langchain/ as the reference implementation.
Naming Conventions¶
| Pattern | Convention | Example |
|---|---|---|
| GraphQL ObjectType | <Entity>Type |
PolicyType, AgentEndpointType |
| GraphQL Mutation | <Verb><Entity> |
CreatePolicy, UpdateAgentEndpoint |
| GraphQL Query class | <Domain>Query |
PolicyQuery, AgentQuery |
| REST view | <Entity>View |
EvaluateView, RegisterView |
| Service | <Domain>Service or <Domain>Engine |
PolicyEngine, ContentScannerService |
| Celery task | <verb>_<entity> |
process_events, generate_compliance_report |
Common Field Names¶
| Model | Correct | Wrong |
|---|---|---|
AgentEndpoint |
agent_id (slug) |
endpoint_id |
Policy |
scope_type |
target_type |
AgentEndpoint |
api_key_hash + api_key_prefix |
api_key |
| All models | tenant_id (string) |
organization_id, org_fk |
Auth in Development¶
By default in local dev, auth validation uses a mock resolver (AUTH_MODE=dev) that accepts any token and assigns it to a test tenant. Set real values in .env to test against Client Cove or standalone auth.
Git Workflow¶
- No co-authorship messages in commits
- No rebasing
- Commit directly to
main