-
-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
iris-ng/
├── source/
│ ├── app/
│ │ ├── blueprints/ # HTTP layer (auth, ACL, I/O validation)
│ │ │ ├── pages/ # Jinja-rendered pages
│ │ │ └── rest/
│ │ │ ├── case/ # Legacy verb-based API (/case/..., /manage/...)
│ │ │ └── v2/ # REST-style API (/api/v2/...)
│ │ ├── business/ # Business logic (no HTTP, no DB)
│ │ ├── datamgmt/ # DB access (SQLAlchemy ORM only)
│ │ ├── iris_engine/
│ │ │ ├── ai/ # All AI orchestrators
│ │ │ └── working_timeline/ # Working-timeline parsers + resolvers
│ │ ├── models/ # SQLAlchemy ORM models
│ │ ├── alembic/ # Schema migrations
│ │ └── resources/
│ │ └── ai_prompts/ # Markdown system-prompt files
│ └── iris_misp_sync_module/ # MISP sync module (iris module wheel)
├── ui/
│ ├── src/ # Svelte 5 + Vite bundled pages
│ └── public/assets/ # Static assets (vendored libs, CSS, plain JS)
├── docker/
│ ├── webApp/ # App + worker Dockerfile + entrypoint
│ ├── nginx/ # TLS + proxy (conf baked at build, not volume-mounted)
│ └── db/ # Postgres Dockerfile
└── scripts/ # Dev and maintenance helpers
Imports may only flow downward. Cross-layer imports in either direction are forbidden.
blueprints (HTTP — auth, ACL, request/response I/O)
↓
business (domain logic — no HTTP objects, no ORM session)
↓
datamgmt (DB access — SQLAlchemy session, ORM queries)
Both are served by the same Flask app. Prefer /api/v2/ for new clients.
| API family | Path prefix | Response shape | Notes |
|---|---|---|---|
| Legacy |
/case/..., /manage/...
|
{status, message, data} wrapper |
v2.0.2 spec |
| REST v2 | /api/v2/... |
Payload directly as JSON body — no wrapper | iris-ng additions |
Client gotcha: a v2 endpoint returns the payload directly — reading resp.data (the
legacy shape) returns undefined. Read the body as-is: Array.isArray(resp) ? resp : (resp.data ?? resp).
IRIS-NG v2.5.0-beta.1 introduced a Svelte 5 + Tailwind + shadcn-svelte frontend under
ui/. Legacy page templates are Jinja (source/app/blueprints/pages/).
-
Bundled JS (
ui/src/pages/*.js→ Vite → image) — changes require an image rebuild -
Static assets (
ui/public/assets/) — baked into the image at build time; deploy patches withdocker cpand rebuild source to survive the next rebuild -
Jinja templates (
source/app/.../templates/) — volume-mounted; take effect afterdocker restart iriswebapp_app(gunicorn caches templates;auto_reload = False)
Iris modules are Python wheels loaded at runtime. Each module registers hook handlers
that celery dispatches via task_hook_wrapper.
Celery fork-safety: worker processes use NullPool (configured in source/app/__init__.py
when "worker" in sys.argv). This prevents Postgres connection fd corruption when prefork
children inherit a shared QueuePool. Do not add per-module db.session.remove() shims —
they detach ORM objects handed in by task_hook_wrapper.
| Hook | Trigger |
|---|---|
on_postload_case_create |
Case created |
on_postload_case_update |
Case updated |
on_postload_ioc_create |
IOC created |
on_postload_ioc_update |
IOC updated |
Alembic manages schema versions. The begin_transaction() wrapper in
source/app/alembic/env.py is critical — upstream left it commented out, causing
all column-add migrations to silently fail. iris-ng restores it.
New tables are also created via db.create_all() at startup (before Alembic runs),
so ORM __table_args__ is the authoritative place for CHECK constraints and UNIQUE
constraints — don't rely on migration files alone for these.
All authenticated POST/PUT/DELETE to /api/v2/ require an X-CSRFToken header.
Read the token from <input id="csrf_token"> (rendered by {{ form.hidden_tag() }}
on every authenticated page). For multipart/form-data requests, append csrf_token
as a form field instead of (or in addition to) the header.
| ID | Level |
|---|---|
| 1 | TLP:RED |
| 2 | TLP:AMBER |
| 3 | TLP:GREEN |
| 4 | TLP:CLEAR |
| 5 | TLP:AMBER+STRICT |