Domain Model
Domain-Driven Design layer with Entity, AggregateRoot, and ValueObject traits. Pure domain logic with no platform dependencies.
The domain module implements Aleph's domain model using Domain-Driven Design (DDD) principles. It defines the core vocabulary of the system — what a Session is, what a Task means, how Intent is structured — independent of any infrastructure or framework concerns.
Design Philosophy
The domain layer follows three DDD principles:
- Pure domain logic — No database queries, no HTTP calls, no file I/O
- Type-safe boundaries — Invalid states are unrepresentable via the type system
- Explicit invariants — Domain rules are enforced at construction time, not checked later
This module has no dependencies on tokio, axum, or any async runtime. It can be tested with plain #[test] functions, no runtime needed.
Core Traits
The domain layer ships only three minimal marker traits (src/domain/mod.rs) that establish the ubiquitous language — they specify identity and equality semantics, not fields.
Entity
Objects with a unique, immutable identity that remains constant through state changes.
pub trait Entity {
type Id: Eq + Clone + std::fmt::Display;
fn id(&self) -> &Self::Id;
}Entities are compared by identity, not by attributes. Two Session objects with the same session_id are the same entity, even if their other fields differ.
Examples: Session, Task, Agent
AggregateRoot
The entry point of an aggregate — a consistency boundary that enforces domain invariants across multiple entities.
pub trait AggregateRoot: Entity {}The AggregateRoot is the only object that outside code can reference directly. All other objects in the aggregate are accessed through it. Note: the actual trait does not carry a version() method — version numbers are provided by individual aggregate implementations when needed; the trait itself does not require one.
Examples: TaskGraph (contains Tasks and Dependencies), MemoryFact (contains ContextAnchors)
ValueObject
Immutable objects defined entirely by their attributes. Two ValueObjects with the same attributes are equal and interchangeable.
pub trait ValueObject: Eq + Clone {}ValueObjects have no identity — they are pure data. Changing any attribute creates a new ValueObject. Note: the actual trait does not carry a validate() method — validation lives in the constructing function (parse, don't validate) or in the use-site that needs the rule, not in the trait contract.
Examples: TaskStatus, FactType, GuestScope, FileContent
Domain Types
Credentials
Credentials is an enum, not a struct (src/a2a/domain/security.rs).
pub enum Credentials {
BearerToken(String),
ApiKey(String),
OAuth2Token(String),
None,
}The Debug impl prints every variant as [REDACTED] so secrets never reach logs. Credentials is consumed by the A2A protocol adapters during remote handshakes; it does not live in this crate's domain module.
TrustLevel
The skill module uses TrustLevel to decide whether a skill can be installed (src/skill/guard.rs).
pub enum TrustLevel {
Builtin, // Shipped with Aleph — always trusted.
Trusted, // From a curated/known publisher.
Community, // Arbitrary third-party (clawhub default).
}The decision is made by install_allowed(ThreatLevel, TrustLevel); it is not auto-inferred from network origin.
FileContent
The A2A payload form for file content (src/a2a/domain/message.rs).
pub struct FileContent {
pub name: Option<String>,
pub mime_type: Option<String>,
pub bytes: Option<String>, // Base64-encoded
pub uri: Option<String>,
}validate() (pub const, returns Result<(), &'static str>) only enforces that exactly one of bytes or uri is set — neither nor both is rejected. MIME parsing, filename escaping, and non-empty checks are handled at the serialization layer, not here.
Bounded Contexts
Aleph's domain is organized into bounded contexts, each with its own ubiquitous language:
| Context | Key Types | Location |
|---|---|---|
| Memory | MemoryFact, ContextAnchor, NoteRelationArg | src/memory/ |
| Identity | IdentityContext, GuestScope, Role | shared/protocol/src/auth.rs, src/gateway/session_manager/ |
| Skill | TrustLevel, ThreatLevel | src/skill/guard.rs |
| A2A | Credentials, FileContent, SecurityScheme | src/a2a/domain/ |
| Loop-Graph Governance | NodeKind, EdgeKind | src/loop_graph/types.rs |
| Goal | Goal, GoalStatus, PursuitMode, GateOutcome | src/goal/types.rs |
| Artifact | ArtifactRecord | src/artifacts/store.rs |
| Team | TaskStatus, ArtifactType | src/teams/artifacts.rs |
Cross-context references go through explicit mapping layers — IdentityContext lives in aleph_protocol (cross-process), while src/memory/ carries its own MemoryFact and ContextAnchor; the two do not share types directly.
Design Patterns in Practice
Newtype for Type Safety
pub struct SessionId(String);
pub struct TaskId(String);
// Cannot accidentally pass a TaskId where SessionId is expected
fn load_session(id: SessionId) -> Option<Session> { ... }Enum State Machines
TaskStatus (src/teams/artifacts.rs) takes the form:
pub enum TaskStatus {
Pending,
InProgress,
Completed,
Blocked,
Failed,
}Illegal states are unrepresentable — a Task cannot be both Pending and Completed.
Parse, Don't Validate
impl Email {
pub fn parse(input: &str) -> Result<Self, ValidationError> {
// Rejects invalid input at the boundary
// Returns a typed Email that is guaranteed valid
}
}Code Location
src/domain/— Three marker traits plus theskillsubmoduledocs/reference/DOMAIN_MODELING.md— Extended domain modeling guide
See Also
- Architecture Overview — Where domain fits in the 5-layer model
- Design Patterns — Context, Newtype, Builder patterns
- Session Management — Identity and session domain concepts
26.7.x Addendum
POE Context Retired
The POE (Principle-Operation-Evaluation) runtime architecture was retired in 26.5+. Its "intelligence" migrated to system prompt templates (R9); success definition, validation, and evaluation are now naturally covered by a single LLM call. The Harness does not judge completion (R10 "Don't #3") or participate in evaluation.
Historical POE types have been removed from the current source tree. Use Git history for the corresponding version rather than referring to a current module path.
New Bounded Contexts
- Loop-Graph Governance context (
src/loop_graph/) — team nodes / audit ring / objective ACLs / victory-claim watchers / built-inloop-auditor - Goal context (
src/goal/) — persistent goals managed byGoalStore, with the maker/checkerGateOutcomesplit - Artifact context (
src/artifacts/) — deliverables produced byartifact_publish,MAX_ARTIFACT_BYTES = 50 MiB/MAX_ARTIFACTS_PER_SESSION = 200 - Signing context (
src/identity/) — per-agent Ed25519 key (AgentKeystore) plus hash-chained signed ledger (AgentLedger), verified byverify_chain
Harness File and Line-Count Ratchets
In 26.7.x, src/harness/ is locked at 12 files, and its line count is gated by CEILING in src/harness/tests/budget.rs. The source constant is the only authority; this page does not copy a current count that will drift. See Architectural Redlines R10.