# Architecture
This document defines what `nythos-core` contains today and what must stay out.
The crate already includes implemented domain types, auth/session/RBAC models,
orchestration services, and boundary ports. These notes describe the constraints
those modules follow.
## Boundary
`nythos-core` is the pure core of Nythos.
It owns:
- domain types and invariants
- auth, session, and RBAC business rules
- orchestration logic for core auth flows
- trait contracts for required external capabilities
It does not own:
- HTTP handlers, routers, middleware, or status codes
- SQL queries, ORM models, migrations, or database drivers
- Redis clients, cache adapters, queues, or event buses
- email, SMS, OAuth providers, or webhook integrations
- concrete signing libraries or password-hasher implementations
- product-specific admin models or deployment concerns
## Five Layers
1. Domain primitives
- `AuthError`
- `NythosResult`
- typed IDs
- `Email`
- `Password`
2. Identity
- `User`
- `Tenant`
3. Auth
- password-hash concepts
- access-token and claims concepts
- token purpose
- register, login, refresh, and revoke orchestration services
4. Session + RBAC
- `Session`
- `RefreshToken`
- `Role`
- `Permission`
- `RoleAssignment`
- `RoleRegistry`
5. Ports
- repository traits
- cryptographic service traits
- revocation-checking traits
- small domain-facing helper payloads for boundary operations
## Dependency Direction
Dependencies point inward.
- `domain` and `error` depend on nothing domain-external
- `auth`, `session`, and `rbac` depend on primitives and shared domain types
- `ports` reference domain types because they describe contracts around them
- infrastructure depends on the core, not the other way around
Practical rule: core code may call trait methods defined in `ports`, but it may
not import infrastructure implementations.
## Module Boundaries
## `error`
Contains the single core error enum `AuthError` and the standard result alias
`NythosResult`.
## `domain`
Contains foundational types and identity models shared across the rest of the crate.
Contains:
- typed ID newtypes over `Uuid`
- `UserId`, `TenantId`, `SessionId`, `RoleId`
- `Email`
- `Password`
- `User`
- `UserStatus`
- `Tenant`
- `TenantSettings`
## `auth`
Contains auth-specific concepts and orchestration logic.
Contains:
- `PasswordHash`
- `AccessToken`
- `Claims`
- `TokenPurpose`
- `RegisterService`
- `LoginService`
- `RefreshService`
- `RevokeSessionService`
- `RevokeAllSessionsService`
## `session`
Contains the session model and refresh-token concepts.
Contains:
- `Session`
- `RefreshToken`
## `rbac`
Contains tenant-scoped authorization concepts.
Contains:
- `Role`
- `Permission`
- `RoleAssignment`
- `RoleRegistry`
## `ports`
Contains pure trait contracts only.
Contains:
- `UserRepository`
- `RoleRepository`
- `SessionStore`
- `PasswordHasher`
- `TokenSigner`
- `RevocationChecker`
- `NewUser`
- `UserCredentials`
- `RoleAssignmentInput`
- `SessionRecord`
- `RefreshTokenRotation`
No default adapters belong here.
## Role Of Ports
Ports are contracts for capabilities the core needs but must not implement directly.
Examples:
- loading and persisting users
- hashing and verifying passwords
- signing and verifying access tokens
- storing, rotating, and revoking sessions
Ports are not extension points for arbitrary plugin systems. They exist because
the core must remain deployment-agnostic.
## Current Design Gap
Verified `Claims` and `RevocationChecker` do not line up completely today.
- `Claims` currently carry subject, tenant, purpose, and issue/expiry timestamps
- `Claims` do not currently carry `SessionId`
- `RevocationChecker` requires `SessionId`
- request-time revocation after token verification therefore cannot be driven by verified `Claims` alone
This is a documented current design gap, not a behavior change request in this
issue.
## Why One Crate
`nythos-core` is kept as a single library crate with internal modules because:
- the current scope is small enough to keep in one crate
- the boundaries are logical, not packaging-driven
- splitting now would add maintenance cost without solving a real problem
- internal module boundaries are enough to keep the architecture clean
A split can still happen later if compile-time, ownership, or public API pressure
makes it useful. Until then, keep it flat.