Skip to content
TezcatAIPublic

About

An operational toolkit for the identification and risk profiling of unmanaged local AI infrastructure.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

743 Commits

Folders and files

Repository files navigation

Tezcat β€” offensive risk profiling for unmanaged local AI infrastructure

Β Tezcat

An offensive toolkit for the identification and risk profiling of unmanaged local AI infrastructure.

Version License Backend Frontend Deploy


What is Tezcat?

Self-hosted AI assistants, copilots, and bot-style integrations are landing inside organisations faster than their security teams can inventory them β€” let alone audit what they will actually do when prompted. Tezcat is a security tool built for that gap. Point it at the AI agents you (or someone in your org) is running, run a battery of probes against them, and get back a typed risk profile: what each endpoint can be made to do, mapped onto industry risk frameworks, with drift comparison between probe runs so you can tell when behaviour changes underneath you.

Tezcat drives targets over eight channels (HTTP/REST + OpenAPI, OpenAI-compatible, Anthropic-compatible, Open WebUI, MCP, Telegram, Mattermost, and Email/IMAP), classifies every response with rule-based parsers (no LLM in the analysis loop), and stores the result as queryable, diffable state.

Highlights

  • Session-oriented, C2-style plugin contract. One AccessPlugin ABC supports both request/response channels (REST, OpenAI/Anthropic-compatible, Open WebUI) and async session channels (MCP, Telegram, Mattermost, Email). See ADR 0001.
  • Multi-framework risk model. Capabilities are cross-linked to MITRE ATLAS, OWASP Top 10 for LLM Apps (2025), OWASP Top 10 for Agentic Apps (2026), NIST AI RMF, and AVID simultaneously, with no schema migration to add more. See ADR 0002.
  • Drift, not just findings. The first probe run is the baseline; every subsequent run produces a typed diff across seven classes (new / lost / confirmed / evidence-changed / regressed-to-inconclusive / still-absent / severity-changed). See ADR 0008.
  • No LLM in the parser. Response classification is rule-based (regex-with-negation, slot-fill, structured-JSON, voting + confidence). Tezcat never feeds prompts back to an LLM to interpret responses, which keeps results reproducible, auditable, and free of an injection surface. See ADR 0003.
  • Security-first defaults. Encrypted credential store (Fernet + HKDF + env-var key, gated decryption, never returned by the API), append-only audit log enforced at the database grant level, single-tenant deployment with three roles (Admin / Operator / Auditor). See ADRs 0005 and 0006.

Who is this for?

  • Internal red teams / AppSec engineers auditing the LLM-powered tools their developers have spun up.
  • Blue teams / SOC who need a continuous-monitoring signal on AI agents that don't show up in any conventional asset inventory.
  • Security researchers wanting a reproducible, scriptable harness for behavioural probing of AI endpoints, with results stored in a queryable, diffable form.

Tezcat is not a public-cloud SaaS, a guardrail / runtime defence, or an LLM evaluation harness for model quality. It is an offensive auditing tool for assets you are authorised to test.

Warning

Only run Tezcat against AI endpoints you own or have explicit written authorisation to probe. Probing third-party services may violate their terms of service or applicable computer-misuse law. You are responsible for your use of this tool.

Features

Subsystem Status
Endpoint inventory + tagging Yes
Encrypted credential store (Fernet, HKDF, env-var key) Yes
AccessPlugin contract + plugin loader Yes
REST / OpenAPI channel Yes
OpenAI-compatible channel Yes
Anthropic-compatible channel Yes
Open WebUI channel (tool-enabled chat) Yes
MCP (Streamable HTTP) channel Yes
Telegram Bot channel Yes
Mattermost bot channel Yes
Email / IMAP+SMTP channel Yes
Curated prompt library + ProbeProfiles (explicit_list + filter_spec resolvers) Yes
Probe orchestration (Celery, live concurrency + rate limiting, per-prompt retries) Yes
Rule-based response parsing (regex / slot-fill / structured-JSON / voting) Yes
Typed CapabilityType catalog + per-(endpoint, capability_type) findings Yes
Risk frameworks: MITRE ATLAS, OWASP LLM, OWASP Agentic, NIST AI RMF, AVID Yes
Drift comparison between probe runs (7 drift classes) Yes
Scheduled probes (cron, Celery beat) Yes
In-app + external notifications (Slack / webhook / email) Yes
Append-only audit log (DB-grant enforced) Yes
RBAC (Admin / Operator / Auditor), TOTP MFA, account lockout Yes
React + TanStack frontend (setup β†’ endpoint β†’ probe β†’ findings β†’ drift) Yes
Nginx TLS termination + security headers Yes
Docker Compose dev + prod overlays Yes
Playwright golden-path E2E test Yes

See the ADRs in docs/adr/ for the load-bearing decisions behind each subsystem, and Limitations for what Tezcat does not do.

Architecture at a glance

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      HTTPS       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Browser     β”‚ ───────────────► β”‚   nginx      β”‚  TLS, /api proxy, SPA fallback
β”‚  (React SPA) β”‚                  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                         β”‚
                                         β–Ό
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚   Django + DRF (web)                     β”‚
                  β”‚   β€’ Auth / RBAC / MFA                    β”‚
                  β”‚   β€’ Service layer (no view-layer ORM)    β”‚
                  β”‚   β€’ OpenAPI 3.1 (drf-spectacular)        β”‚
                  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚                     β”‚
                         β–Ό                     β–Ό
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚  Postgres  β”‚        β”‚   Redis    β”‚
                  β”‚  (state +  β”‚        β”‚  (broker + β”‚
                  β”‚   audit)   β”‚        β”‚   cache)   β”‚
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                                              β”‚
                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                          β–Ό                                     β–Ό
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚  Celery worker(s)      β”‚          β”‚  Celery beat (singleton)β”‚
              β”‚  β€’ Probe orchestrator  β”‚          β”‚  β€’ Scheduled probes     β”‚
              β”‚  β€’ AccessPlugin loader β”‚          β”‚  β€’ Retention pruning    β”‚
              β”‚  β€’ Rule-based parsers  β”‚          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚  β€’ Drift engine        β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β–Ό
                  Target AI endpoints
                  (REST / OpenAI / Anthropic / Open WebUI / MCP / Telegram / Mattermost / Email)

The decisions behind every box on this diagram are recorded as ADRs in docs/adr/.

Requirements

  • Docker 24+ and the Docker Compose v2 plugin (docker compose, not docker-compose).
  • Git (with submodule support β€” see below).
  • GNU Make (optional, for the convenience targets).
  • ~4 GB free RAM and a few GB of disk for images and the Postgres volume.

Everything runs in containers; you do not need a local Python or Node toolchain to deploy. (You do for backend/frontend development β€” see CONTRIBUTING.md.)

Install and deploy

1. Clone (with submodules)

The upstream risk-framework data (MITRE ATLAS, OWASP LLM Top 10, AVID) is vendored as git submodules under assets/. Clone recursively so the framework seeder can load the full data sets. The backend Docker image bakes the MITRE ATLAS data in at build time (assets/atlas-data), so docker build fails fast if that submodule is not checked out. Running the backend directly from a checkout without the submodules seeds a built-in baseline stub instead.

git clone --recurse-submodules https://github.com/<your-fork>/Tezcat.git
cd Tezcat

# Already cloned without --recurse-submodules? Pull them now:
git submodule update --init --recursive

2. Configure the environment

cp deploy/env.example .env

Then edit .env. At minimum, rotate every secret away from its dev default. See the configuration reference for the full list.

# Generate a Django secret key (50+ chars):
python3 -c 'import secrets; print(secrets.token_urlsafe(50))'

# Generate the credential-store data key:
python3 -c 'import secrets; print(secrets.token_urlsafe(32))'

Set, at a minimum:

  • DJANGO_SECRET_KEY β€” a fresh random value.
  • POSTGRES_PASSWORD and REDIS_PASSWORD β€” rotate the dev defaults; keep DATABASE_URL / REDIS_URL in sync with them.
  • TEZCAT_CREDENTIAL_KEY_V1 β€” the key that encrypts every stored credential. If you lose or change it, all stored credentials become undecryptable. Keep it out of version control and back it up alongside (never inside) the database.
  • DJANGO_ALLOWED_HOSTS and DJANGO_CSRF_TRUSTED_ORIGINS β€” your real hostname(s) for anything beyond localhost.

3. Bring up the stack

The dev overlay runs Django via runserver, mounts the source for live reload, and binds ports to 127.0.0.1 only.

make dev          # build + start: postgres, redis, web, worker, nginx (frontend built into the nginx image)
make logs         # follow all containers

On first boot the web container runs migrations, seeds the framework + prompt catalogs, and collects static files automatically (TEZCAT_AUTO_MIGRATE=1, the default). This can take a couple of minutes; the container is marked healthy only once it finishes. To run those steps by hand instead, set TEZCAT_AUTO_MIGRATE=0 and use:

make migrate      # python manage.py migrate
make seed         # seed_frameworks + seed_prompts

Without make, the equivalent is:

docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.dev.yml up -d --build

4. Create the first admin

Open the app and complete the first-run setup wizard, which creates the initial Admin account:

open https://localhost      # accept the self-signed certificate warning

The wizard only appears while no user exists (GET /api/v1/auth/setup-status β†’ needs_setup). Alternatively, create a superuser from the CLI:

docker compose -f deploy/docker-compose.yml exec web python manage.py createsuperuser

You are now ready to register an endpoint, add a credential, build or pick a probe profile, run a probe, and review findings and drift.

5. TLS

The stack terminates TLS at nginx using nginx/tls/tezcat.crt and nginx/tls/tezcat.key (this directory is gitignored β€” no key material is committed). Generate a self-signed certificate for dev/internal use in either of two ways:

# From the CLI:
cd nginx/tls && ./generate-cert.sh testing            # or: ./generate-cert.sh production <hostname>

…or from the UI as an Admin: Settings β†’ Deployment networking β†’ Use self-signed SSL certificate, set the hostname, save, then docker compose restart nginx.

For internet-facing production, replace the self-signed cert with one issued by your corporate / internal CA. Full generation, verification, and rotation steps are in docs/runbook/tls.md and nginx/tls/README.md.

Production

The prod overlay (deploy/docker-compose.prod.yml) runs Django under gunicorn (5 workers), scales Celery workers to two replicas, sets DJANGO_DEBUG=False, and publishes ports 80/443 on all interfaces. Bring it up with:

docker compose \
    -f deploy/docker-compose.yml \
    -f deploy/docker-compose.prod.yml \
    up -d --build

Before exposing it:

  • Set DJANGO_DEBUG=False, a real DJANGO_ALLOWED_HOSTS, and matching DJANGO_CSRF_TRUSTED_ORIGINS.
  • Replace the self-signed certificate with a CA-issued one.
  • Rotate all secrets and store TEZCAT_CREDENTIAL_KEY_V1 in your secrets manager, not in a checked-in .env.
  • Put the host behind your own network controls; Tezcat is single-tenant and assumes a trusted operator network. See ADR 0006.

Install, upgrade, and backup steps are covered in the sections above; TLS certificate management and deployment-networking changes have dedicated runbooks under docs/runbook/.

Scheduled probes (Celery beat)

Scheduled probes, retention pruning, and deleted-drift purging are driven by a periodic tick from a Celery beat process. The compose stack ships a dedicated beat service, so these fire automatically once the stack is up β€” no extra steps. The schedule itself (probe dispatch every 60s, daily pruning) is defined in backend/tezcat/celery.py.

Important

beat is a singleton. Never scale it past one replica or every periodic task fires multiple times. The worker service is the one you scale for throughput. On-demand probes do not depend on beat.

Configuration reference

All runtime configuration is via the .env file consumed by the web and worker containers. Deployment-time options that an Admin can change at runtime (probe limits, lockout thresholds, retention windows, MFA policy, networking/TLS) live in the in-app Settings area instead.

Variable Required Default (dev) Purpose
DJANGO_SECRET_KEY Yes β€” Django cryptographic signing key. Use 50+ random chars.
DJANGO_DEBUG β€” True Set False in production.
DJANGO_ALLOWED_HOSTS Yes (prod) localhost,127.0.0.1,tezcat.local Comma-separated hostnames Django will serve.
DJANGO_CSRF_TRUSTED_ORIGINS Yes (prod) localhost origins Comma-separated origins trusted for CSRF (scheme + host).
TEZCAT_AUTO_MIGRATE β€” 1 Run migrate + seed + collectstatic on web boot. Set 0 to manage manually.
DATABASE_URL Yes postgres://tezcat:devpw@postgres:5432/tezcat Postgres DSN. Keep credentials in sync with the POSTGRES_* vars.
POSTGRES_DB Yes tezcat Database name.
POSTGRES_USER Yes tezcat Database user.
POSTGRES_PASSWORD Yes devpw Database password β€” rotate this.
REDIS_URL Yes redis://:devredispw@redis:6379/0 Celery broker + cache DSN.
REDIS_PASSWORD Yes devredispw Redis password β€” rotate this.
TEZCAT_CREDENTIAL_KEY_V1 Yes β€” Data key for the encrypted credential store. Never commit it; losing it makes stored credentials undecryptable.
CORS_ALLOWED_ORIGINS β€” empty (same-origin) Comma-separated origins for cross-origin API access. Leave empty for the bundled single-origin deployment.

Note

Postgres and Redis are docker-internal only (no published ports). In the dev overlay, web is published on 127.0.0.1:8000 and nginx on 127.0.0.1:80/443. Never bind these to 0.0.0.0 on an untrusted network.

Operating

  • Health checks. GET /api/v1/readyz reports readiness (used by the container healthcheck); GET /api/v1/auth/me confirms an authenticated session.
  • Upgrades. Pull the new revision (git pull --recurse-submodules), rebuild, and restart: docker compose ... up -d --build. With TEZCAT_AUTO_MIGRATE=1, migrations run on web boot.
  • Backups. Back up the tezcat_pgdata Postgres volume and your TEZCAT_CREDENTIAL_KEY_V1 β€” both are required to restore. The encrypted credential ciphertext is useless without the key, and the key is useless without the database.
  • Retention. Raw-response and probe-run retention windows are configured in Settings β†’ Data retention and enforced by the daily prune task (requires Celery beat β€” see above). 0 means keep forever.
  • Audit. Every privileged action is recorded in an append-only audit log; UPDATE/DELETE are revoked on the audit table at the Postgres grant level and never re-granted.

Repository layout

.
β”œβ”€β”€ assets/          Vendored risk-framework data as git submodules (ATLAS, OWASP LLM, AVID)
β”œβ”€β”€ backend/         Django + DRF + Celery + AccessPlugin host
β”‚   β”œβ”€β”€ apps/        One bounded context per app (auth, audit, inventory, credentials,
β”‚   β”‚                access, prompts, frameworks, probes, findings, drift, notifications,
β”‚   β”‚                settings, core)
β”‚   β”œβ”€β”€ plugins/     First-party AccessPlugins (rest_openapi, openai_compat, anthropic_compat,
β”‚   β”‚                open_webui, mcp, telegram, mattermost, email_imap)
β”‚   β”œβ”€β”€ api/         Generated OpenAPI 3.1 schema (committed; diffable in PRs)
β”‚   └── tezcat/      Django project (settings, urls, asgi, wsgi, celery)
β”œβ”€β”€ frontend/        React 18 + Vite + TS + TanStack + shadcn/ui SPA
β”œβ”€β”€ deploy/          docker-compose.{yml,dev.yml,prod.yml} + env.example
β”œβ”€β”€ nginx/           nginx.conf + conf.d + Dockerfile + TLS material (gitignored)
β”œβ”€β”€ scripts/         set_version.py and other maintenance helpers
β”œβ”€β”€ tests-e2e/       Playwright golden-path test (login β†’ probe β†’ findings)
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ adr/         Architecture Decision Records (load-bearing decisions)
β”‚   β”œβ”€β”€ api/         API contract notes (the schema itself lives in backend/api/)
β”‚   β”œβ”€β”€ data-model/  ER diagrams + per-app model walkthroughs
β”‚   β”œβ”€β”€ plugins/     How to write an AccessPlugin
β”‚   β”œβ”€β”€ runbook/     Operator runbooks (TLS, deployment networking)
β”‚   └── threat-model/ Security posture + threat-model notes
β”œβ”€β”€ Makefile         dev / test / lint / migrate / seed targets
β”œβ”€β”€ VERSION.txt      Single source of truth for the app version
└── LICENSE          MIT

Documentation

  • CONTRIBUTING.md β€” development setup, code style, and the test/lint gates.
  • docs/adr/ β€” Architecture Decision Records. Read these to understand why Tezcat is shaped the way it is. Start with 0001 (plugin contract), 0004 (capability/drift model), 0008 (scope and drift).
  • docs/plugins/ β€” how to write a new AccessPlugin, grounded in the contract in backend/apps/access/contract.py and the worked example in plugin-template/.
  • docs/runbook/ β€” operator runbooks for TLS certificate management and deployment-networking changes.
  • In-app docs β€” once deployed, the Docs section in the app provides concept guides (endpoints, credentials, prompts, profiles, probe runs, findings, drift, frameworks, RBAC, audit, settings).

Security model

A short summary; the deployed security-header posture and threat-model notes live in docs/threat-model/.

  • Single-tenant, single-team (ADR 0006). No multi-workspace isolation. Three roles: Admin, Operator, Auditor. TOTP MFA and account lockout are configurable in Settings.
  • Session-cookie + CSRF auth. No JWTs in localStorage. The frontend fetch wrapper reads the csrftoken cookie automatically for non-GET requests.
  • Credentials are encrypted at rest with Fernet + HKDF; the data key is held in TEZCAT_CREDENTIAL_KEY_V1 (env var only, never in the database). Decryption is gated to plugin probe execution; the API surface never returns plaintext. See ADR 0005.
  • Audit log is append-only at the Postgres grant level: a migration revokes UPDATE and DELETE on the audit table. Tests in apps/audit/tests/ enforce this; the right is never granted back.
  • Plugins are pure. They never touch the DB, never parse responses, never classify capabilities. The orchestrator does all of that.
  • No LLM in the analysis loop. Responses are classified by deterministic rule-based parsers, so a malicious target response cannot prompt-inject the classifier.
  • Service-layer pattern. Views and Celery tasks call apps/<app>/services.py; views never write to the ORM directly. Services re-check authorisation against the actor argument.

To report a vulnerability, follow the private disclosure process in SECURITY.md rather than opening a public issue.

Limitations

Tezcat is deliberately scoped. Known boundaries of the current implementation:

  • Single-tenant. No multi-workspace or multi-team isolation; the deployment assumes one trusted operator team behind your own network controls. See ADR 0006.
  • First-party plugins only. The bundled channels run in-process; there is no sandboxed runtime for third-party plugins.
  • Rule-based scoring. Capability scoring is threshold-based rule parsing. The adaptive-testing and item-response-theory measurement layer is documented as a research direction in ADR 0012, not as a shipped feature.

Contributing

Tezcat began as a thesis project and is now developed in the open. Issues and pull requests are welcome:

  • File issues for bugs and feature ideas. For security findings, follow the private disclosure process in SECURITY.md rather than opening a public issue.
  • To add an AccessPlugin for a channel Tezcat doesn't yet support, start with docs/plugins/.
  • Development setup, code style, and the test/lint gates are documented in CONTRIBUTING.md.

License

MIT Β© 2026 TezcatAI.

About

An operational toolkit for the identification and risk profiling of unmanaged local AI infrastructure.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages