SentnelOpsSentnelOpsbeta

Advanced & self-hosting

The Python SDK and CLI, streaming to your SIEM, performance characteristics, and running the whole stack on your own infrastructure.

Python SDK & CLI

The SDK ships in the repository (sdk/, installable with pip install -e sdk) and wraps the two calls most integrations need:

python
from sentnelops import AIRClient

client = AIRClient(api_key="snops_key_…",
                   base_url="https://api.sentnelops.com")

agent = client.register(name="ci-release-bot", owner="platform-team",
                        environment="dev", created_by="ada@acme.dev",
                        allowed_mcp=["github-mcp"])

decision = client.is_permitted(agent["id"], "github-mcp", tool="delete_repo")
# Decision(permitted=False, reason="tool delete_repo is denied on github-mcp")
shell
export SNOPS_API_KEY=snops_key_…
export SNOPS_BASE_URL=https://api.sentnelops.com

sentnelops register --name prod-billing --owner finance-team \
    --env production --allowed-mcp stripe-mcp --risk-level high
# ✓ Agent registered — prints agent_id + token (shown once)

SIEM & automation

IntegrationHow
Splunk / Datadog / SentinelSchedule GET /mcp-calls/export.csv?days=1 with the api key — the CSV is import-ready
Real-time pipelineConsume wss://…/ws/calls?key=<api-key> — JSON events under a second after each call
AlertingSlack webhook + SMTP settings; unset = zero egress, events logged locally
Compliance evidence/agents/export.csv and /reports/governance on a cron

Performance characteristics

PropertyDetail
Proxy overheadMeasured p99 under 2 ms added latency — call logging is fully off the response path
Identity cacheThe proxy caches agent identity + upstream resolution for a few seconds (configurable). Org policy is cached per org with the same TTL — a save lands immediately on the writing replica and within one TTL elsewhere; the platform-default files hot-reload on mtime change
Rotation latencyToken rotation invalidates old tokens within the identity-cache TTL (default 5 s)
Fail-open invariantIn observe mode, no failure inside SentnelOps ever drops a customer call

Self-hosting

The stack is a single container image (FastAPI + asyncpg) plus Postgres 16 — VPC native, zero data egress by design:

cp .env.example .env        # set SNOPS_JWT_SECRET at minimum
docker compose up --build   # Postgres 16 + API :8000 + Caddy :80/:443
VariableDefaultPurpose
SNOPS_DATABASE_URLlocal postgresasyncpg DSN
SNOPS_JWT_SECRETchange itSigns agent + user JWTs; rotating it invalidates every token at once
SNOPS_RATE_LIMIT_PER_MINUTE600Per-credential fixed window (per minute)
SNOPS_POLICIES_PATHpoliciesPlatform-default policy YAML directory (hot-reloaded)
SNOPS_PROXY_IDENTITY_TTL_SECONDS5.0Proxy identity + org policy cache; 0 disables
SNOPS_SLACK_WEBHOOK_URLunsetSlack alerts + digest
SNOPS_ALERT_EMAIL_TO / _FROM, SNOPS_SMTP_*unsetEmail alerts (STARTTLS)
SNOPS_ENFORCEMENT_FAIL_MODE_DEFAULTopenLast resort when the policy store itself is unreadable
SNOPS_CORS_ORIGINSlocalhost:3000Browser origins allowed to call the API directly
Ops rhythm: run make sweep (TTL + review checks) on a daily cron, keep policy YAML in version control, and rotate agent tokens on schedule. Migrations are idempotent (make migrate).

CIMD document shape

GET /cimd/{agent_id}.json
{
  "client_id": "/cimd/<agent_id>.json",
  "client_name": "<agent name>",
  "sentnelops": {
    "agent_id":    "<uuid>",
    "owner":       "<team>",
    "environment": "dev | staging | production",
    "risk_level":  "low | medium | high",
    "status":      "active | suspended | …",
    "allowed_mcp": ["…"]
  }
}

Unknown or malformed ids return 404. The document never contains secrets — it is safe to resolve from anywhere.