- Docs
- Architecture & operations
- Multi-tenancy & data model
Multi-tenancy & data model
Every record in Jutsu belongs to exactly one organization, and that organization ID scopes all access. The boundary holds in both data planes: PostgreSQL rows carry an organization column, and OpenSearch events live in per-organization indices. This page explains the tenancy model and the entities it spans.
How tenancy works
Jutsu is multi-tenant by organization. A tenant is an organization, and all of its data — users, incidents, cases, credentials, events — is scoped to that organization's ID.
- Transactional scoping. Tables in PostgreSQL carry an
organization_id, and queries filter by it so one tenant never reads another's rows. - Search isolation. Normalized events are written to per-organization indices in OpenSearch, so a tenant's events occupy their own index space rather than a shared one.
- Onboarding by invitation. People join an organization through invitations with an explicit lifecycle (pending, accepted, expired, revoked); members hold roles within that organization.
Per-organization indices
The ingest path writes each normalized event into a weekly, per-organization index:
org-{organizationId}-{year}-w{week}-eventsWeekly rollover keeps each index bounded in size and makes age-based archival and deletion straightforward — old weeks can be dropped without touching current data. Reads use the org-{organizationId}-*-events pattern to span an organization's weeks while staying inside its tenant boundary.
Core entities
The entities below are the backbone of the data model. Field-level detail lives in the schema; this is the operator's map.
| Entity | Key fields | Notes |
|---|---|---|
| Organizations | id, settings, per-severity SLA targets | The tenant. Owns all other records. |
| Organization members | organization, user, role | Roles include owner, admin, member, and analyst tiers (L1–L3). |
| Invitations | email, status | Lifecycle: pending → accepted / expired / revoked. |
| Users | email, role, auth provider | Auth via Google or local; platform-level role separate from org membership. |
| Incidents | INC-XXXX, severity, confidence, correlation | Severity critical–info; confidence 0–100; status active / resolved / closed; correlation reason, signals, and evidence. |
| Incident alerts | alert, severity, correlation score | The alerts grouped into an incident. |
| Cases | status, priority, severity | Status OPEN → IN_PROGRESS → PENDING → RESOLVED → CLOSED; priority P1–P4. |
| Case evidence | kind, ref | Evidence of kind file, alert, or event attached to a case. |
| Case comments | type, body | Comments and notes on a case. |
| AgentSOAR credentials | provider, resources | Connected action providers and their resources. |
| AgentSOAR executions | status, capability, dedup key | Records of dispatched response actions, with idempotency. |
| Copilot conversations | organization, user | Assistant chat threads, scoped per user and org. |
| Copilot messages | role, content | Messages within a conversation (user, assistant, system). |
| Audit events | actor, action, occurred at | System and user-action audit trail. |
Relationships at a glance
Organization
├── Members ──▶ Users (via roles)
├── Invitations
├── Incidents (INC-XXXX)
│ └── Incident alerts
├── Cases (P1–P4, OPEN→CLOSED)
│ ├── Evidence (file / alert / event)
│ └── Comments
├── AgentSOAR credentials ──▶ Executions
├── Copilot conversations ──▶ Messages
└── Audit events