Inbox Agent Implementation Architecture and Repository Structure
1. Purpose
This document defines the recommended application code organization, repository structure, module boundaries, dependency direction, naming conventions, provider adapter layout, testing organization, worker organization, database organization, and implementation guardrails for Inbox Agent.
The goal is to provide a repository structure that:
- Works well with Cursor and coding agents
- Preserves the architecture already defined
- Avoids a monolithic Next.js application
- Avoids premature microservices
- Keeps provider-specific code isolated
- Makes domain logic easy to test
- Supports web and future native iOS clients
- Supports Charter-generated implementation work
- Keeps infrastructure, migrations, tests, and documentation organized
2. Repository Philosophy
Use:
ONE REPOSITORY
+
CLEAR DOMAIN BOUNDARIES
+
MULTIPLE RUNTIME ENTRY POINTS
rather than many repositories initially.
The repository should behave like a modular monolith.
3. Recommended Repository Shape
inbox-agent/
├── apps/
│ ├── web/
│ └── workers/
│
├── packages/
│ ├── domain/
│ ├── application/
│ ├── api-contracts/
│ ├── providers/
│ ├── security/
│ ├── ai/
│ ├── persistence/
│ ├── jobs/
│ ├── observability/
│ ├── config/
│ └── testing/
│
├── database/
│ ├── migrations/
│ ├── seeds/
│ └── scripts/
│
├── infrastructure/
│ ├── environments/
│ ├── workers/
│ ├── storage/
│ └── monitoring/
│
├── tests/
│ ├── e2e/
│ ├── security/
│ ├── provider-contracts/
│ ├── ai-evals/
│ └── migration/
│
├── fixtures/
│ ├── mail/
│ ├── providers/
│ ├── security/
│ └── migration/
│
├── docs/
│ ├── requirements/
│ ├── architecture/
│ ├── decisions/
│ ├── operations/
│ └── runbooks/
│
├── scripts/
│
├── .github/
│ └── workflows/
│
└── README.md
4. Monorepo Tooling
A JavaScript/TypeScript workspace should support:
Shared types
Shared lint rules
Shared test utilities
Incremental builds
Dependency-aware CI
Exact workspace technology may be selected during implementation.
Examples include npm, pnpm, or equivalent workspace tooling.
Do not select tooling solely because it is fashionable; prioritize reliability and Cursor/tooling compatibility.
5. apps/web
apps/web contains the Next.js application.
Responsibilities:
React UI
Routes
Server/client presentation concerns
Authentication entry points
API transport adapters where colocated
OAuth callback endpoints
Webhook ingress endpoints
It should NOT contain the core business rules.
6. Web Directory Example
apps/web/
├── app/
│ ├── (authenticated)/
│ │ ├── home/
│ │ ├── needs-me/
│ │ ├── respond/
│ │ ├── waiting/
│ │ ├── security/
│ │ ├── search/
│ │ ├── rules/
│ │ ├── identity/
│ │ ├── migration/
│ │ └── settings/
│ ├── api/
│ └── auth/
│
├── components/
├── features/
├── hooks/
├── lib/
└── tests/
7. Feature-Oriented UI Organization
UI features should preferably group:
Components
Queries
Mutations
View models
Feature-specific hooks
around product capabilities.
Example:
features/waiting/
features/security-review/
features/rules/
Avoid a giant global components folder containing domain logic.
8. apps/workers
Worker runtime entry points live here.
Example:
apps/workers/
├── src/
│ ├── sync-worker.ts
│ ├── mail-processing-worker.ts
│ ├── command-worker.ts
│ ├── security-worker.ts
│ ├── ai-worker.ts
│ ├── migration-worker.ts
│ ├── reconciliation-worker.ts
│ └── maintenance-worker.ts
Workers should compose shared packages rather than duplicate business logic.
9. Worker Responsibilities
Workers are runtime hosts.
They should NOT contain domain implementation beyond orchestration/bootstrap.
Example:
Worker receives job
→ invokes Application Service
→ Application Service invokes Domain/Adapter
10. packages/domain
This is the core provider-independent domain model.
Contains:
Entities
Value objects
Enums
Domain services
Domain invariants
State transition logic
11. Domain Must Not Depend On
packages/domain should not import:
Next.js
Microsoft Graph SDK
Google APIs
IMAP library
PostgreSQL client
AI SDK
Queue SDK
Vercel SDK
This is a critical dependency rule.
12. Domain Module Structure
Example:
packages/domain/src/
├── mail/
├── conversations/
├── attention/
├── identities/
├── rules/
├── learning/
├── security/
├── approvals/
├── commands/
├── migration/
├── projects/
└── shared/
13. Logical Message Model
The authoritative implementation must distinguish:
MailMessage
Logical message/content identity
MessageInstance
Physical provider copy
Suggested domain structure:
domain/mail/
├── mail-message.ts
├── message-instance.ts
├── recipient.ts
├── attachment.ts
├── conversation.ts
└── provenance.ts
14. Provider Independence
The domain may expose:
MailAccount
MailIdentity
MailMessage
MessageInstance
but must not expose concepts such as:
GmailMessage
GraphMessage
IMAPMessage
outside provider modules.
15. packages/application
Contains use cases and application services.
Responsibilities:
Queries
Commands
Workflow orchestration
Authorization coordination
Policy evaluation
Service transactions
Event publication
16. Application Module Example
packages/application/src/
├── mail/
│ ├── get-message.ts
│ ├── archive-message.ts
│ └── search-mail.ts
├── conversations/
├── attention/
├── rules/
├── security/
├── approvals/
├── migration/
├── identity/
├── providers/
└── system/
17. Application Depends On Interfaces
Application services should depend on interfaces such as:
MailRepository
ConversationRepository
ProviderAdapter
JobQueue
AuditWriter
SecurityScanner
ModelGateway
Concrete implementations belong elsewhere.
18. packages/api-contracts
Contains transport-independent DTOs and schemas for:
Web API
Future iOS API
Agent tools
Webhook-normalized commands
Possible contents:
Request DTOs
Response DTOs
Error contracts
Validation schemas
OpenAPI generation
19. API Contracts Are Not Database Entities
Never expose persistence objects directly through public API.
Use dedicated DTOs.
20. packages/providers
Contains provider-specific implementations.
Recommended layout:
packages/providers/src/
├── common/
├── microsoft/
├── gmail/
└── icloud/
21. Provider Interface
Canonical interface may live in application or dedicated shared contracts.
Implementations:
MicrosoftGraphMailAdapter
GmailMailAdapter
ICloudMailAdapter
22. Microsoft Provider Module
Suggested contents:
microsoft/
├── graph-client.ts
├── auth.ts
├── message-mapper.ts
├── folder-mapper.ts
├── sync.ts
├── subscriptions.ts
├── commands.ts
└── adapter.ts
23. Gmail Provider Module
Suggested:
gmail/
├── gmail-client.ts
├── auth.ts
├── message-mapper.ts
├── label-mapper.ts
├── history-sync.ts
├── watch.ts
├── commands.ts
└── adapter.ts
24. iCloud Provider Module
Suggested:
icloud/
├── imap-client.ts
├── auth.ts
├── mime-mapper.ts
├── uid-sync.ts
├── folder-mapper.ts
└── adapter.ts
Initial iCloud implementation remains read-oriented.
25. Provider Mapping
All provider modules should translate external objects into canonical provider DTOs before application logic sees them.
26. Provider Capability Registry
Provider capabilities should be implemented centrally.
Example:
Microsoft:
read, search, archive, move, categories, drafts, send, push, delta
Gmail:
read, search, archive, labels, drafts, send, push/history
iCloud:
read, folders, incremental IMAP, limited write depending future decision
27. packages/security
Contains content-security components.
Suggested structure:
packages/security/src/
├── gateway/
├── html/
├── urls/
├── attachments/
├── quarantine/
├── policies/
└── findings/
28. Security Isolation
The package contains security logic, but untrusted heavy parsing may execute in isolated worker processes/containers.
The repository boundary and deployment boundary are separate concerns.
29. HTML Sanitization
Suggested module:
security/html/
├── sanitizer.ts
├── remote-content-policy.ts
├── link-rewriter.ts
└── safe-render-model.ts
30. URL Inspection
Suggested:
security/urls/
├── parser.ts
├── policy.ts
├── redirect-resolver.ts
├── reputation.ts
└── ssrf-guard.ts
31. Attachment Inspection
Suggested:
security/attachments/
├── metadata.ts
├── type-detector.ts
├── archive-inspector.ts
├── scanner-interface.ts
├── sandbox-interface.ts
└── policy.ts
32. packages/ai
AI behavior belongs behind model-independent interfaces.
Structure:
packages/ai/src/
├── gateway/
├── classification/
├── summarization/
├── drafting/
├── search-intent/
├── rule-suggestions/
├── schemas/
└── evaluations/
33. AI Gateway
Provider-specific model SDK usage should remain inside gateway implementations.
The rest of the application uses operations such as:
classifyMessage()
summarizeConversation()
generateDraft()
interpretSearch()
34. AI Output Schemas
Structured output schemas belong in a stable package and are reused by:
Runtime validation
Tests
Evaluation harness
API diagnostics
35. packages/persistence
Contains repository implementations and persistence mapping.
Example:
packages/persistence/src/
├── db/
├── repositories/
├── mappings/
├── transactions/
└── outbox/
36. Repository Interfaces vs Implementations
Interfaces belong with application/domain contracts.
Implementations belong in persistence.
Example:
MailRepository
→ interface
PostgresMailRepository
→ implementation
37. Database Schema
Database migration files should remain outside ORM-generated opaque state wherever practical.
Schema changes must be reviewable.
38. Database Naming
Suggested naming convention:
snake_case tables/columns
UUID primary keys
created_at
updated_at
Domain object naming can remain TypeScript-style.
39. Database Domains
Tables should group logically around:
accounts
mail
classification
rules
learning
security
commands
migration
audit
operations
but remain normalized rather than creating one schema/table per service without need.
40. JSON Policy
Use JSON only for:
Provider raw metadata
Flexible capability data
Structured AI trace metadata
Diagnostics
Non-core extensible payloads
Do not store the core relational model as JSON blobs.
41. packages/jobs
Contains durable job contracts and orchestration.
Example:
packages/jobs/src/
├── definitions/
├── handlers/
├── scheduling/
├── retries/
├── idempotency/
└── dead-letter/
42. Job Definitions
Jobs should be typed and versioned.
Example:
SyncMailboxJobV1
ClassifyMessageJobV1
ExecuteMailCommandJobV1
MigrateBatchJobV1
43. Job Handlers
Handlers should be thin:
Deserialize
Validate
Load application service
Execute
Record result
44. packages/observability
Shared instrumentation:
logging
metrics
tracing
correlation IDs
error classification
Provider/application code should use these abstractions rather than independent logging conventions.
45. packages/config
Contains:
Typed configuration schemas
Environment validation
Feature flags
Policy defaults
Runtime limit definitions
No secrets committed to repository.
46. packages/testing
Reusable test infrastructure:
Mock providers
Factories
Fixture loaders
Fake clocks
Fake queues
Domain builders
Assertions
47. Fake Clock
Time-dependent features such as:
Waiting
Due dates
Stale follow-up
Token expiration
Subscription renewal
should use injectable clock abstractions.
This avoids brittle tests.
48. Provider Mocks
Mock providers should satisfy the same contract as real providers where practical.
49. database/migrations
All production schema migrations live here and are version-controlled.
50. database/seeds
Only synthetic/reference data.
Never production email.
51. infrastructure
Contains infrastructure definitions and deployment configuration.
Suggested:
infrastructure/
├── environments/
│ ├── development/
│ └── production/
├── workers/
├── storage/
├── queues/
└── monitoring/
52. Infrastructure Is Separate From App Configuration
Do not place rules, mailbox roles, user preferences, or identities in infrastructure code.
53. tests/e2e
Contains product workflow tests.
Examples:
respond-to-waiting.spec.ts
system-alert.spec.ts
quarantine.spec.ts
identity-hygiene.spec.ts
rule-learning.spec.ts
migration.spec.ts
54. tests/security
Contains hostile/adversarial testing.
Example:
prompt-injection/
html/
urls/
attachments/
authorization/
secrets/
55. tests/provider-contracts
Common contract suite run against:
Mock Microsoft
Mock Gmail
Mock iCloud
Optional dedicated live development accounts
56. tests/ai-evals
Stores evaluation harness code and expected structured outcomes.
Sensitive user-derived evaluation data should not necessarily be committed unencrypted to source control.
57. fixtures/mail
Synthetic RFC-style messages.
Organize by:
human
financial
travel
system
commercial
security
58. fixtures/security
Examples:
malicious-html
tracking-pixel
phishing-links
ssrf-links
macro-documents
zip-bombs-safe-fixture
prompt-injection
Do not include real malware binaries in the normal repository.
59. Documentation Layout
Recommended:
docs/
├── requirements/
├── architecture/
├── decisions/
├── operations/
└── runbooks/
60. Requirements Directory
Holds authoritative product artifacts such as:
Functional Requirements
Security Specification
Migration Specification
Configuration Specification
61. Architecture Directory
Contains:
Technical Architecture
Processing Pipeline
Deployment Architecture
Canonical Data Model
API Contracts
62. Architecture Decision Records
docs/decisions should contain focused ADRs for decisions with meaningful alternatives.
Example:
ADR-001 PostgreSQL as Canonical Store
ADR-002 Microsoft Graph Instead of IMAP
ADR-003 Gmail API Instead of IMAP
ADR-004 Logical Message vs Message Instance
ADR-005 Provider Body as Source of Truth
63. Avoid Duplicate Documentation
An ADR should capture the decision and rationale.
It should not reproduce entire requirements documents.
Documentation should link rather than duplicate.
64. Operations Documentation
Examples:
Provider reconnect
OAuth scope change
Restore database
Run reconciliation
Pause automation
Recover dead-letter queue
65. Runbooks
Critical operational runbooks:
Provider auth expired
Graph subscription failed
Gmail watch expired
Security scanner unavailable
Unexpected mass action
Migration reconciliation failure
Suspected credential compromise
66. Dependency Direction
Recommended dependency direction:
UI
↓
API/Application
↓
Domain
Infrastructure Implementations
↑
Application Interfaces
The domain should sit at the center.
67. Forbidden Dependency Example
Prohibited:
domain/rules
imports
providers/gmail
Rules should know canonical fields, not Gmail.
68. Dependency Enforcement
Use lint/build rules where practical to enforce module boundaries.
Do not rely solely on developer discipline.
69. Import Aliases
Stable workspace aliases may include:
@inbox/domain
@inbox/application
@inbox/providers
@inbox/security
@inbox/ai
@inbox/persistence
@inbox/config
@inbox/testing
70. Naming Conventions
Use descriptive domain names.
Prefer:
MessageInstance
AttentionState
RuleExecution
SecurityFinding
over vague names such as:
Data
Item
Thing
Handler2
71. Command Naming
Application commands should use verbs:
ArchiveConversation
SetAttentionState
CreateDraft
ApproveRule
ReleaseQuarantine
72. Query Naming
Queries should describe retrieval:
GetNeedsMe
SearchMail
GetConversation
GetProviderHealth
73. Event Naming
Events use past tense:
MessageDiscovered
MessageQuarantined
CommandSucceeded
UserCorrectionRecorded
74. Provider Mapping Naming
Use explicit mapping names:
mapGraphMessageToProviderMessage()
mapGmailMessageToProviderMessage()
Do not leak raw SDK objects into application services.
75. Error Model
Shared application errors should have canonical codes.
Examples:
NOT_FOUND
POLICY_DENIED
APPROVAL_REQUIRED
PROVIDER_UNAVAILABLE
AUTH_EXPIRED
CONFLICT
RATE_LIMITED
Provider-specific exceptions should map into these.
76. No Raw Provider Errors to UI
External provider errors may contain implementation details or sensitive data.
Convert them before client exposure.
77. Transaction Boundaries
Database transactions should be owned in the application/persistence layer.
Domain objects should not start database transactions themselves.
78. Outbox Implementation
Outbox support belongs in persistence/jobs.
Application services write:
state change
+
outbox event
atomically.
79. Idempotency
Idempotency helpers should be reusable rather than reimplemented in every worker.
80. Correlation IDs
Correlation context should flow through:
API
Application service
Job
Provider adapter
Audit
Logs
81. Audit Integration
Do not scatter direct audit table writes throughout code.
Use a centralized audit interface.
82. Policy Gateway
All consequential mutations should route through one application-level policy gateway.
Avoid:
if approved:
gmail.archive()
inside feature code.
83. Agent Tool Gateway
Agent tools should exist in a dedicated boundary.
Suggested:
application/agent-tools/
├── search-mail.ts
├── summarize.ts
├── request-archive.ts
├── request-draft.ts
├── explain.ts
└── propose-rule.ts
84. AI Cannot Import Provider Modules
The AI package must not directly import provider adapters for mutation.
Agent requests pass through application services.
85. Security Cannot Bypass Application Policy
Security services may quarantine/block according to security policy, but ordinary provider mutations still use appropriate command/service boundaries.
86. Rule Engine Package Location
Rule evaluation belongs primarily in domain/application.
Provider actions produced by rules become canonical action requests.
87. Rule DSL
Keep rule condition/action definitions as structured types.
Do not allow arbitrary JavaScript execution from stored rules.
88. Regex Safety
If regex is supported:
limit complexity
limit execution duration
validate patterns
89. Search Architecture
Search should have a dedicated application interface capable of combining:
PostgreSQL metadata
Provider-native content search
Future semantic index
Do not let UI call providers separately.
90. Future Semantic Search
If embeddings are later added, implement behind a search abstraction.
No core entity should require vector storage.
91. Future Native iOS Client
Repository may later add:
apps/ios/
or a separate native repository if mobile lifecycle justifies it.
The backend contract should not change merely because iOS is added.
92. Shared Generated Clients
api-contracts may generate:
TypeScript client
Swift client
from OpenAPI or equivalent machine-readable schemas.
93. Build Boundaries
Each package should expose intentional public entry points.
Avoid deep imports such as:
@inbox/domain/src/mail/internal/private-helper
94. Package Public APIs
Use package-level index exports to control dependencies.
95. Private Implementation
Internal helper modules should not automatically become cross-package dependencies.
96. Circular Dependency Prevention
The workspace should fail CI on problematic circular dependencies where tooling permits.
97. CI Pipeline
Recommended jobs:
install
typecheck
lint
unit-tests
contract-tests
security-tests
build
database-validation
preview-e2e
98. Changed-Package Optimization
CI may later run only affected packages for speed while retaining full regression runs before production as needed.
99. PR Discipline
Implementation should favor:
One coherent ticket/change
One branch
One PR
where feasible.
PRs should identify:
Requirement
Implementation
Tests
Risks
Schema changes
100. Source of Truth
Charter/Jira may drive implementation planning, but repository documentation remains authoritative for architecture and requirements.
Tickets should link to relevant sections rather than copy entire specifications.
101. README
Root README should remain concise.
Suggested contents:
Product summary
Architecture summary
Repo structure
Development setup
Common commands
Documentation links
Do not turn README into a duplicate architecture specification.
102. Developer Setup
Development bootstrap should ideally provide one command or short workflow to start:
Web
Local database
Mock queue
Mock providers
Synthetic fixtures
103. Mock-First Local Development
Default developer mode should use:
Mock Microsoft
Mock Gmail
Mock iCloud
Mock AI or deterministic AI fixtures
unless explicitly enabling development integrations.
104. Development Mail Viewer
A useful internal development tool may inspect synthetic messages and their processing history.
This should never require production data.
105. Schema Generation
If using ORM/schema tooling, generated code should be checked for reproducibility.
Database schema must remain understandable independently of generated client APIs.
106. Data Access Rules
Avoid arbitrary SQL inside UI or route handlers.
Data access should flow through repositories/application queries.
107. Performance Escape Hatch
Complex PostgreSQL queries may use carefully written SQL where appropriate.
Architecture should not force an ORM abstraction when direct SQL is materially better.
108. Migration Scripts
Historical mail migration scripts should not live as random one-off scripts.
Migration logic belongs in the Migration Service.
Operational scripts should invoke supported application interfaces.
109. scripts
Use only for controlled developer/admin utilities.
Examples:
validate-env
seed-fixtures
generate-api-client
run-evaluations
verify-migrations
110. No Dangerous Ad Hoc Scripts
Avoid scripts capable of mass deleting or moving production mail outside the command/policy/audit path.
111. Code Generation
Generated code should live in clearly marked locations.
Never manually edit generated files without documented process.
112. Formatting and Style
Use one shared formatting and lint configuration across TypeScript packages.
113. Strict TypeScript
Prefer strict TypeScript configuration.
Avoid widespread any.
Provider SDK boundaries may use unknown/raw types only until validated/mapped.
114. Runtime Validation
TypeScript types alone are insufficient for:
API requests
Provider responses
AI responses
Job payloads
Configuration
Validate untrusted runtime data.
115. Date/Time Handling
Use UTC internally for timestamps.
Convert to user timezone only at presentation or explicit scheduling boundaries.
116. Email Address Handling
Use dedicated normalization utilities.
Do not lowercase or transform fields blindly where RFC/provider semantics may matter.
Canonical comparison strategy should be defined centrally.
117. Hashing Utilities
Duplicate hashes, content fingerprints, and attachment hashes should use centralized implementations.
118. Testing Proximity
Unit tests should generally live near implementation.
Large cross-cutting suites remain in top-level tests.
119. Security Fixture Safety
Potentially dangerous fixture files should be inert/synthetic whenever possible.
Do not store active malware in source control merely for realism.
120. ADR Requirement
Create ADRs when implementation chooses among meaningful alternatives such as:
Queue technology
Database hosting
Authentication provider
AI provider
Object storage
HTML sanitizer
Security scanner
121. Implementation Decision vs Requirement
Do not rewrite a requirement simply because a library changes.
Example:
Requirement:
Durable background processing
Implementation:
Specific queue technology
Keep these separate.
122. Versioning
API, events, jobs, and database schema should each have explicit compatibility strategies.
They do not need to share one version number.
123. Application Version
Application release version may use:
major.minor.patch.build
or another agreed convention.
124. Changelog
Maintain a release changelog describing:
New capabilities
Changed behavior
Migration notes
Security changes
Breaking changes
Avoid listing every internal refactor.
125. Observability From Development
Logging/tracing interfaces should work locally with console/test implementations and production with managed implementations.
126. Repository Acceptance Criteria
This repository architecture is acceptable when:
- Next.js presentation logic is separated from the domain.
- Workers reuse application/domain packages.
- Provider SDKs remain isolated in provider modules.
- The logical
MailMessage/ physicalMessageInstancedistinction is represented explicitly. - Security code has a dedicated module.
- Untrusted heavy parsing can deploy separately.
- AI model implementations remain behind a gateway.
- AI cannot bypass application policy.
- Persistence implementations remain separate from domain logic.
- API DTOs remain separate from database entities.
- Jobs are typed and versioned.
- Provider mocks are first-class.
- Synthetic fixtures are source controlled.
- Security and AI evaluation suites have dedicated locations.
- Documentation is organized without duplication.
- ADRs capture implementation decisions.
- CI can enforce package boundaries.
- Production-changing scripts cannot bypass the command/audit pipeline.
- The repository supports both web and future iOS clients.
- The structure is simple enough for an initial modular monolith.
