Technical Architecture

Atamaia is a three-layer platform built on .NET 10, ASP.NET Core, PostgreSQL with pgvector, and EF Core.

                        ┌─────────────────────────────────────────┐
                        │          Interaction Layer              │
                        │  REST API │ MCP Server │ CLI │ Agents   │
                        └──────────────────┬──────────────────────┘
                                           │
                        ┌──────────────────┴──────────────────────┐
                        │          Core Services Layer            │
                        │  Memory │ Identity │ Hydration          │
                        │  Communication │ Projects/Tasks         │
                        │  Standing Rules │ AI Routing │ Mirror   │
                        │  Graph │ Billing │ Auth                 │
                        └──────────────────┬──────────────────────┘
                                           │
                        ┌──────────────────┴──────────────────────┐
                        │          Autonomic Layer                │
                        │  Wingman │ Consolidation │ Guardian     │
                        └──────────────────┬──────────────────────┘
                                           │
                        ┌──────────────────┴──────────────────────┐
                        │          PostgreSQL + pgvector          │
                        └─────────────────────────────────────────┘

Mind (Atamaia.Mind) still holds in-process experience and cognitive types. Those are not public REST. Snapshot/shape/cognitive HTTP routes 404 on prod (2026-08-13). Forgotten shapes are a MemoryType on the work-memory store, not a separate table.

Canonical REST host: https://api.atamaia.ai. MCP is a thin adapter over the same services (D12). See AI routing, Agents, Autonomic layer.


Solution Structure

Atamaia.sln
├── src/
│   ├── Atamaia.Core            -- Domain models, interfaces, enums, events
│   ├── Atamaia.Services        -- Business logic (service implementations)
│   ├── Atamaia.Mind            -- In-process cognitive/experience types (not public REST)
│   ├── Atamaia.Mind.Migration  -- Database migrations
│   ├── Atamaia.Adapters.Api    -- REST API controllers, auth middleware
│   ├── Atamaia.Adapters.Mcp    -- MCP server adapter
│   ├── Atamaia.Server          -- Host application (Program.cs, DI wiring)
│   ├── Atamaia.Autonomic       -- Background jobs (Wingman, consolidation, Guardian, …)
│   ├── Atamaia.Cli             -- CLI tool
│   └── Atamaia.Web             -- React 19 SPA (Vite, Tailwind v4, shadcn/ui)
├── tests/                      -- xUnit tests against real PostgreSQL
├── docs/                       -- Repo-side docs (published guides live in the docs DB)
├── sql/                        -- SQL scripts
└── tools/                      -- Build/deployment tools

Atamaia.Web.old exists on disk as a leftover tree. It is not a solution project.


Database Schema

Base Entity

Every tenant-scoped table inherits from AtamaiaEntity:

id              bigint PK (auto-increment)
guid            uuid (unique, auto-generated)
tenant_id       bigint FK -> tenants
created_at_utc  timestamp
updated_at_utc  timestamp
created_by_id   bigint FK -> users
updated_by_id   bigint FK -> users
is_active       boolean
is_deleted      boolean (soft delete)

A few stores are deliberately not AtamaiaEntity: SystemSetting, interest/support registrations, and the log/audit archive tables (system-scoped housekeeping must see every tenant).

Core Tables

Table Purpose Notes
tenants Multi-tenant isolation plan_id, Stripe ids, force_readable_personal_memories, primary_user_id
users Human accounts username, email, password_hash, role
identities AI / Human / System personas user_id (not linked_user_id), presence, type
refresh_tokens JWT refresh tokens token_hash, expires, revoked
identity_api_keys Per-identity credentials key_hash, prefix atamaia_…, scopes, expiry
identity_hints Contextual reminders surfaced at hydrate
identity_tool_profiles Per-identity tool tiers safe / opt-in / blocked

Personality, memory-config, messaging-policy, and hydration-config remain identity-owned configuration (see Core concepts).

Memory and rules

Four surfaces, not one table. Detail: Memory surfaces, Standing rules.

Table Purpose
memories Work-memory store (encrypted content, type, provenance, embedding)
memory_tags / hebbian_links / memory_recalls Tags, associative links, recall log
personal_memories (+ tags/links) Owner-only parallel store
agent_memories Run-learned knowledge that decays
sessions / session_states Session work half vs state half (stateVisible)
standing_rules / standing_rule_memory_links In-force instructions, grounding + evidence

Forgotten shapes are rows in memories with type = ForgottenShape, written by consolidation. There is no forgotten_shapes entity.

Project tables

Table Purpose Notes
projects Work containers key, name, status
project_tasks Hierarchical tasks kind, subsystem, is_launch_blocker required at create — Tasks
task_dependencies DAG BFS cycle detection (D14)
task_notes Append-only activity indexed by search
docs / doc_versions Knowledge base published guides live here
facts Key-value knowledge history / as-of routes exist

Communication, graph, AI, agents

Area Tables
Messages messages, message_recipients, session_handoffs
Code graph graph_nodes, graph_edges, graph_driftsCode graph
AI routing ai_providers, ai_models, ai_route_configs, ai_model_groups, ai_model_group_members, tenant_provider_credentials, ai_router_callsAI routing
Chat chat_sessions, chat_messages
Agents role definitions, runs, events, escalations, tool profiles, feedback, councils, run notes, agent tasks — Agents
Mirror reflections, training pairs/datasets/runs, checkpoints
Org / RBAC org units + members/locations/contacts; roles / role_permissions
Channels / MCP / connectors bindings, proxies, external connectors
Billing subscriptions, invoices, line items, events, quota boosts
Observability system_logs, log_subscriptions, audit_events (+ archive tables)
Cognitive (in-process) cognitive_identities, cognitive_interactions, consolidation_logsno public REST

Gone from the live API (and from current entities): devices / device_challenges, experience_snapshots as a REST-backed table.


Design Decisions

D3: Both Long ID and GUID on Every Table

Every AtamaiaEntity has both id (bigint, auto-increment, for fast internal joins) and guid (UUID, for external references and API stability). Internal code uses the long ID. External APIs accept either.

Why: Long IDs are faster for joins and indexes. GUIDs are stable for external references, idempotency, and cross-system integration.

D5: 3NF Everywhere, Enums Backed by Lookup Tables

Full third normal form. Enums are stored as integers, seeded as lookup tables (SeedLookupTable<T> in OnModelCreating). No magic strings.

Why: Clean data, clear schema, no ambiguity. The performance cost of normalization is negligible with proper indexing.

D7: Multi-Tenant from the Start

TenantId on every AtamaiaEntity. EF Core global query filters ensure tenant isolation at the SQL level. Not middleware, not application logic — the query itself is tenant-scoped.

A declared system scope (ITenantProvider.IsSystemScope) is the explicit exception: housekeeping jobs that must see every tenant. Writes in that scope require an explicit TenantId. Detail: Multi-tenancy.

Why: Retrofitting multi-tenancy is expensive and error-prone.

D12: API-First, MCP Second

REST endpoints are the source of truth. The MCP server wraps services. The API can be tested independently of MCP. Non-AI consumers use REST directly.

Why: An MCP-first approach forces everything to pretend to be an LLM calling tools.

D14: Task Dependencies with BFS Cycle Detection

Adding a dependency triggers BFS from the target. If it can reach back to the source, the add is refused.

Why: Circular dependencies crashed agent loops in testing.

D15: Soft Delete Only, Never Hard Delete

Every delete sets IsDeleted = true. No data is ever removed on the product surfaces. Archive tables exist for log/audit retention; that is not a hard-delete of domain rows.

Why: Prevented data loss multiple times during development. In a memory system, accidental deletion is catastrophic.

D16: Tests Against Real PostgreSQL, Not SQLite/In-Memory

All tests run against a real PostgreSQL instance.

Why: jsonb, tsvector, pgvector, array types, and upsert semantics behave differently in other providers.

Additional Decisions

  • D1: .NET 10 + EF Core for the platform
  • D2: PostgreSQL as the single database (no separate vector DB)
  • D4: EF Core code-first with explicit migrations
  • D6: snake_case column names in PostgreSQL
  • D8: JWT with refresh token rotation for auth
  • D9: BCrypt for password hashing
  • D10: Correlation IDs on every request
  • D11: ApiEnvelope<T> response wrapper on all REST endpoints
  • D13: Permission-based authorization (92 named Permission values as of 2026-08-14)
  • D17: Open Responses as canonical streaming protocol
  • D18: AES-256-GCM encryption for memory content and fact values

Security Model

Authentication

Current public methods:

  1. JWT — Short-lived access tokens with refresh token rotation. Each refresh invalidates the previous token. POST /api/auth/login, POST /api/auth/refresh.
  2. API keys — Identity-level keys minted as atamaia_…, presented as Authorization: Bearer atamaia_…. User-level key hashes also exist on users.
  3. OAuth — Social login (/api/auth/oauth/{github,google,microsoft}) and an authorization-server surface (/oauth/*, /.well-known/*).

Device auth (Ed25519 challenge-response) is not on the live API. /api/auth/device* 404s; there is no Device entity.

Authorization

Permission-based RBAC. Roles aggregate permissions. IPermissionService checks permissions on controller actions. Identity-owned stores add an org-hierarchy read / owner-only write rule on several surfaces — see Memory surfaces.

Encryption at Rest

Memory content and fact values are encrypted with AES-256-GCM using per-tenant key derivation. Titles and keys remain in plaintext for search. Content is decrypted transparently on read. Customer-managed keys are a plan-level claim; confirm availability against your plan.

Multi-Tenant Isolation

Global query filters: WHERE NOT is_deleted AND (system_scope OR tenant_id = @current). TenantId is set on create and marked not-modified on update. See Multi-tenancy.


Streaming Architecture

Chat responses use the Open Responses protocol over Server-Sent Events:

event: response.created
data: {"type":"response.created","response":{...}}

event: response.output_text.delta
data: {"type":"response.output_text.delta","delta":"Hello"}

event: response.completed
data: {"type":"response.completed","response":{...}}

data: [DONE]

Provider adapters normalize upstream formats into this canonical protocol. Same idea on POST /api/ai/chat (non-stream) and the OpenAI-compat surface (POST /v1/chat/completions, POST /v1/responses).


Event System

An in-process event bus (IAtamaiaEventBus) distributes domain events. Clients can subscribe via SSE (/api/events/stream) and WebSockets (/ws/agents, /ws/chat, /ws/events).

Event types follow a dotted naming convention: message.sent, task.status_changed, memory.created.


Infrastructure

Development

Atamaia API:     localhost:5000  (or :5158 in some configs)
Atamaia.Web:     localhost:5174  (Vite dev server, proxies to API)
PostgreSQL:      localhost:5432  (or Docker)

Production

https://api.atamaia.ai          -- REST + OpenAPI (/openapi/v1.json)
https://api.atamaia.ai/mcp      -- MCP (auth required; unauth → 401)
https://aim.atamaia.ai          -- SPA. Not the OpenAPI server.

Caddy reverse proxy handles TLS, compression, and routing.

Local AI

  • Primary inference host: local llama.cpp models — Wingman analysis, summarization, embeddings
  • Secondary inference host: local roster (push-synced). Routing requires enabled AND availableAI routing

The AI routing layer abstracts provider differences. The same POST /api/ai/chat call works for cloud APIs and local llama.cpp instances.