Cortex — Axonis Decision Intelligence Platform Architecture
What the Axonis Decision Intelligence (ADI) platform IS — its architecture, identity model, objects, surfaces, and configuration system. ADI is a federated AI platform for decision intelligence: AI goes to the data, not data to AI. This spec describes the platform from the Cortex point of view; the object schemas it references are authoritative in component.cortex.decision-evidence, and the pack schemas in component.cortex.pack-reference.
Normative Language
| Term | Meaning |
|---|---|
| MUST | Absolute requirement. Violation is a defect. |
| MUST NOT | Absolute prohibition. Violation is a defect. |
| SHOULD | Recommended. Deviation requires documented rationale. |
| MAY | Optional. Implementation choice. |
| OPEN GAP | Behavior unspecified or unimplemented. Requires resolution before production. |
Decision Operating System
Axonis is a decision operating system: SSO identifies the user, packs configure behavior, UDS governs visibility, Cortex enforces rules, Beacon renders the workflow, and DES preserves the evidence chain from signal to sealed decision.
AXONIS DECISION OS — AI goes to the data. Humans attest the decision.
IDENTITY + ACCESS CONFIGURATION
SSO / KEYCLOAK PACKS
users, roles, groups, AUTHORIZATION keys Profile → capabilities
│ JWT Domain → models / governed fields
▼ Experience → UI / vocabulary
BEACON (UI renders only: Monitor, Accountability → rules / routing
Explore, Investigations, Tasks) Templates / Policies
│ MCP initialize(profile_id)
▼
CORTEX (loads packs, executes tools, enforces guardrails, binds session/profile)
│ queries with visibility keys
▼
UDS (sole ABAC authority, federated data access, deny_mode: omit)
Decision Evidence Flow
Signal ──▶ Investigation ── gathers ──▶ Blocks/Evidence (transient → curated/pinned → frozen/hashed)
│
├─ emits append-only Events
├─ spawns Tasks (human handoff during deliberation)
└─ produces Edition (evidence_manifest + narrative_snapshot + content_hash + attestation)
└─ Sealed Decision └─ optional Decision Effects (post-attestation execution only)
OODA / User Surfaces
OBSERVE → ORIENT → DECIDE → ACT
Monitor Explore Investigations Tasks
Monitor = what needs attention (signals/KPIs). Explore = gather and shape evidence (query/pin). Investigations = write and seal the decision (edition/review). Tasks = route human work, not workflow orchestration.
Hard Boundaries / Inviolable Invariants
- UDS is the only ABAC authority — Cortex and Beacon never filter data.
- Packs configure behavior; code must not invent it.
- Events are append-only — no UPDATE, no DELETE.
- Frozen evidence cannot change — SHA256 digest locked forever.
- Editions require frozen evidence — can't attest to moving targets.
- AI may assist; only humans attest.
- "No action" is still a decision — explicit closure required.
- Tasks are deliberation work; Effects are execution work.
Services
| Service | Port | Role |
|---|---|---|
| Beacon (MCP Client) | 8002 | Renders UI, proxies to Cortex. No business logic. |
| Cortex (MCP Server) | 8000 | Loads packs, executes MCP tools, enforces guardrails. |
| REST API | 5000 | CRUD for objects. Schema validated against the wire schema. |
| SSO (Keycloak) | — | Identity, roles, groups, visibility key claims. |
Communication flow: Angular → Beacon /api/mcp → Cortex /mcp (JSON-RPC); Angular → Beacon /api/cortex/chat → Cortex /chat/stream (SSE); UDS queries carry the user's visibility_keys → ABAC filtering at cell level.
System Authority Matrix
Every concern has exactly ONE authoritative owner. If two documents disagree, the authority listed here wins.
| Concern | Authority | NOT Decided By |
|---|---|---|
| User identity, authentication | SSO (Keycloak) | Beacon, Cortex, Packs |
| Capabilities (what a user can do) | Profile Pack → resolved archetype | SSO roles, frontend code |
| Data visibility (which fields a user sees) | UDS ABAC via visibility_keys from JWT | Cortex, Beacon, Profile Pack |
| Decision governance (evidence rules, attestation, review) | Accountability Pack | Experience Pack, frontend code |
| UI structure (nav, vocabulary, KPIs, suggestions) | Experience Pack | Accountability Pack, Cortex |
| Data models (field names, types, governed fields) | Domain Pack | Experience Pack, frontend code |
| Object lifecycles (valid states, transitions) | DES lifecycle specs (component.cortex.investigation-lifecycle, component.cortex.signal-lifecycle, component.cortex.task-and-effect) | Frontend display states, ad-hoc code |
| Implementation rules (code patterns) | component.cortex.developer-rules | Individual developer judgment |
| AI assistant behavior (drift prevention) | SDD skills | Claude's training data |
| Event types and surface boundaries | component.cortex.decision-evidence + component.cortex.task-and-effect | Frontend component organization |
| Wire format (API shapes) | REST objects.yml |
Frontend interfaces, Cortex DTOs |
| Pack schemas and inheritance | pack bundle YAML (loaded into ES by the loader) | Beacon docs, CLAUDE.md |
Identity Terminology
Four concepts developers frequently conflate:
| Term | What It Is | Where It Lives | Example |
|---|---|---|---|
| SSO Role | An identity claim from the IdP — who the user IS | JWT realm_access.roles[] |
RELATIONSHIP_MANAGER |
| Profile | A platform persona binding a user to capabilities, packs, data access | bundle profiles.yaml |
thebank_rm_v1 |
| Archetype | A capability baseline profiles inherit from | bundle archetypes.yaml |
analyst, commander, principal |
| Capability | A specific permission (boolean, resolved from archetype + domain overrides) | Resolved at MCP initialize; string[] on frontend |
edition_manage, edition_attest |
The mapping: SSO Role → matched to Profile (via auth.roles_any) → Profile specifies Archetype → Archetype provides base Capabilities → Domain overrides add/remove. SSO roles do NOT determine capabilities directly.
Canonical Domain Model
Terminology
| Canonical Term | Wire Protocol Name | UI Display | Aliases (DO NOT USE in new code) |
|---|---|---|---|
| Signal | signal |
Configurable via vocab | alert, notification |
| Investigation | insight (wire only) |
Configurable (default "Investigation") | insight, case, analysis, credit review |
| Block | block |
Configurable (default "Evidence") | evidence, card, data point |
| Event | event / insight_event |
Internal; not user-facing | action, log entry, verb |
| Edition | edition / edition_snapshot |
Configurable via vocab | snapshot, decision, sealed record |
| Task | task |
Configurable (default "Tasks") | assignment, work item, inbox item |
| Decision Effect | decision_effect |
Internal | effect, execution, dispatch. NOT YET IMPLEMENTED. |
| Profile | profile |
Internal | persona, user config |
| Pack | varies by type | Internal | config, bundle. JSON config, never code. |
| Visibility Key | visibility_key |
Internal | marking, read key, ABAC key. UDS-level cell security; NOT a role. |
| Federate | federate |
Internal | data source, index, federation node |
Object Graph
Five core objects: Signal, Investigation (wire insight), Block, Event, Edition. One optional extension: Decision Effect (not implemented). Full schemas and identifier formats in component.cortex.decision-evidence#object-model.
Note on identifiers: DES prefixes (blk_, ins_, evt_, edn_, eff_) are the vendor-neutral standard; the Axonis implementation currently uses plain UUIDs in the REST wire format, which is the runtime authority.
Governed Field Convention (_# Suffix)
Fields whose visibility is controlled by UDS ABAC use the _# suffix in wire names, signalling that the field requires a matching visibility_key:
customer_id ← No suffix → visible to all profiles
customer_name_# ← _# → requires FIN_PII_KEY
risk_rating_# ← _# → requires SENSITIVE_RISK_KEY
internal_watchlist_flag_# ← _# → requires WATCHLIST_KEY
Rules: the _# suffix is part of the field name in ES indices, queries, and API responses; UDS applies deny_mode: omit (unauthorized fields silently excluded); domain packs MUST use _# for any field with metadata.sensitivity: governance; Beacon and Cortex MUST NOT strip or add _# suffixes.
Identity and Access
Resolution Chain
IdP (SSO/Keycloak) → JWT access_token (sub, email, roles, groups, AUTHORIZATION keys)
→ Beacon decodes JWT (no signature verify — relies on SSO proxy trust boundary)
→ User selects profile from /api/profiles → Frontend calls MCP initialize(profile_id)
→ Cortex loads profile + domain + experience + accountability packs
→ Cortex stores profile_id in Redis session (keyed by JWT session_id)
→ UDS applies visibility_keys from profile at query time (deny_mode: omit)
Layer Responsibilities
| Layer | Decides | Does NOT Decide |
|---|---|---|
| SSO/IdP | User identity, realm roles, groups, visibility keys | Profile selection, UI rendering |
| Profile Pack | Capabilities, visibility_keys, allowed_models, federate | ABAC enforcement, UI rendering |
| Accountability Pack | Decision types, evidence minimums, attestation, review routing | UI layout, data queries |
| Experience Pack | Nav config, vocabulary, monitor KPIs, explore suggestions | Decision rules, visibility, capabilities |
| Domain Pack | Data models, subject types, vocabulary, signal type configs | Authorization, accountability |
| UDS/ABAC | Cell-level data filtering, visibility enforcement | Authentication, profile selection |
| Beacon | Rendering, navigation, user interactions | Data filtering, workflow rules, capability enforcement |
| Cortex | MCP tool execution, pack loading, guardrail enforcement | Authentication, data filtering, UI rendering |
JWT Token Claims
| Claim | Used By | Example |
|---|---|---|
sub |
Session tracking, actor ID | "f47ac10b-58cc-..." |
session_state / sid |
X-Session-ID header, Redis key | "abc123-session-id" |
preferred_username |
Display, event actor | "Riskmgr@axonis.ai" |
realm_access.roles[] |
Role matching in packs | ["RELATIONSHIP_MANAGER"] |
groups[] |
Group matching in packs | ["/CreditTeam"] |
AUTHORIZATION.READ[] |
UDS visibility (read) | ["INTERNAL_KEY \| FIN_PII_KEY"] |
AUTHORIZATION.WRITE[] |
UDS visibility (write) | ["INTERNAL_KEY"] |
Visibility keys are pipe-delimited within the AUTHORIZATION claim array.
Session Headers
| Header | Source | Purpose |
|---|---|---|
Authorization: Bearer <token> |
localStorage | Authentication |
X-Session-ID |
JWT session_state or sid |
Redis session key |
X-Profile-ID |
Current selected profile | Profile context |
Profile is NOT passed in the chat request body. Cortex reads profile_id from the Redis session set during MCP initialize.
The Pack System
Seven Pack Types
| # | Pack Type | Purpose | Loaded By |
|---|---|---|---|
| 1 | Profile | Capabilities, visibility, models, federate | MCP initialize |
| 2 | Domain | Data models, subject types, field governance (_#) |
MCP initialize |
| 3 | Experience | UI nav, KPIs, vocabulary, explore suggestions | MCP initialize |
| 4 | Accountability | Decision rules, evidence minimums, attestation, signal policies | MCP initialize |
| 5 | Decision Template | Edition structure: evidence, sections, attestation | Referenced by accountability |
| 6 | Task Template | Task structure: type, assignment, SLA, completion | Referenced by accountability |
| 7 | Signal Policy | Signal generation: thresholds, severity, routing | Referenced by accountability |
Full schemas and inheritance in component.cortex.pack-reference; loading mechanics in component.cortex.domain-loading.
Pack Invariants
- Packs are JSON only. No executable content.
- Packs configure behavior; they do not implement it.
- Packs cannot bypass UDS ABAC.
- Packs are versioned (
_v1,_v2, etc.). - Accountability packs govern workflow. Experience packs govern UI. These concerns MUST NOT be mixed.
- Signal policy IDs in accountability packs MUST match actual policy definitions.
- Decision template IDs MUST reference templates that actually exist. OPEN GAP: no validation today.
Surface Behavior
Pages (OODA Mapping)
| Page | Route | OODA Phase | Primary Objects |
|---|---|---|---|
| Home | /home |
Hub | Navigation only |
| Monitor | /monitor |
Observe | Signals, KPIs |
| Explore | /explore |
Orient | Blocks, AI chat |
| Investigations | /investigations/:insightId |
Decide | Investigation with 4 tabs |
| Edition | /edition/:editionId |
Decide | Sealed edition (standalone) |
| Tasks | /tasks |
Act | Task queue |
Legacy routes redirect: /inbox → /tasks, /insights → /investigations. Route params use :insightId (wire protocol name), not :investigationId.
Accountability Enforcement Timeline
Investigation start → entry_modes[] checked (Cortex)
Evidence gathering → no accountability checks (ABAC still applies at UDS layer)
Pin block → no accountability checks (editorial action)
Create edition → min_evidence enforced, allowed_decision_types checked (Cortex)
Freeze edition → require_rationale enforced, content_hash computed (Cortex)
Request review → OPTIONAL. default_reviewers used for assignment (Cortex)
Attest edition → attester role checked, separation of duties enforced (Cortex)
SKIPPED for no-action decisions
Signal Filtering (Three Gates)
For a signal to reach a user, ALL three gates must pass (AND logic): (1) Policy ID ∈ user's accountability.signals.policies[]; (2) Signal severity ∈ user's accountability.signals.severity_filter[]; (3) Signal visibility_keys ∩ user's visibility_keys ≠ ∅.
Task Semantics
Tasks are the ONLY mechanism for human handoff. Worker-queue pattern, NOT a workflow engine: published to a queue for eligible workers; assigned by eligibility (role, group, user), not deterministic routing; no conditional branching, no auto-state-progression.
Surface Enforcement Gap
Surface event boundaries (which events each page may emit) are currently advisory only. Cortex does NOT validate source_surface on incoming events. A buggy or malicious frontend could emit block_created from Monitor or attested from Explore. Recommended fix: attach source_surface to every event and add server-side validation in Cortex. Until then, the allowed-events table in component.cortex.developer-rules#events-by-surface is a frontend correctness contract, not a security boundary.
Open Gaps
| ID | Gap | Priority |
|---|---|---|
| G-01 | No identity/SSO spec document | High |
| G-02 | Signal transition validation not implemented | High |
| G-03 | Template existence validation at pack load | High |
| G-04 | Decision Effects not implemented | Medium |
| G-05 | Escalation path not enforced in code | Medium |
| G-12 | deferred decision type has no template |
High |
| G-13 | decision_attest vs edition_attest inconsistency |
Medium |
| G-14 | Composite score computation status unknown | Medium |
| G-15 | Guardrails inheritance ambiguity (RM has no guardrails section) | Medium |
Depends on: component.cortex.decision-evidence, component.cortex.intelligence, component.cortex.pack-reference
Required by: component.cortex.developer-rules