Skip to main content

CONSTELLATION — System Architecture Specification

Document Version: 1.1 — Architecture Concept Date: 26 February 2026 (revised April 2026) Classification: Internal — For Technical Review Status: Approved — Open questions resolved March 2026. Implementation may proceed. Section 12 updated April 2026 (Stella merger). Companion to: Constellation General Specification v0.3


Table of Contents

  1. Purpose and Scope
  2. Architectural Drivers
  3. Module Rationalisation: From 13 to 7
  4. Monorepo Structure
  5. Shared Platform Packages
  6. Database Architecture
  7. Identity, Tenancy and Access Control
  8. Event-Driven Integration
  9. Deployment Strategy and Enterprise Readiness
  10. AI-Assisted Development Model
  11. Existing Project-Tracker: Migration Strategy
  12. Relationship to Stella Catalog
  13. Patterns Adopted from Open Mercato
  14. Execution Roadmap
  15. Risk Register
  16. Resolved Architecture Decisions

1. Purpose and Scope

This document defines the system architecture for the Constellation platform. It translates the functional specification (Constellation General Specification v0.3) into a concrete technical design that accounts for:

  • A small team (1-2 humans + AI coding agents) building the entire platform
  • The need for each module to deliver standalone value while sharing cross-cutting infrastructure
  • A migration path from SaaS prototype to enterprise-grade sovereign deployment
  • 100% AI-assisted development, which imposes specific constraints on code organisation, context management, and testing strategy

This is a concept document for review. No implementation should begin until the architecture has been discussed, challenged, and approved.

What This Document Decides

  • Repository structure and build system
  • Module boundaries and what gets merged or deferred
  • Shared vs. module-specific code boundaries
  • Database isolation strategy
  • Identity and access control architecture
  • Inter-module communication pattern
  • Build order and execution sequence
  • AI agent development rules

What This Document Does NOT Decide

  • Detailed data models for individual modules (each module will have its own design phase)
  • UI/UX design system details (beyond establishing that packages/ui exists)
  • Specific API endpoint designs
  • Pricing, licensing, or commercialisation strategy

2. Architectural Drivers

These constraints, derived from the specification and practical reality, drive every architectural decision in this document.

D1: Modular Independence with Shared Infrastructure

"Organisations will be able to adopt Constellation module by module." — Spec Section 2

Each module must be deployable and usable independently. However, duplicating authentication, tenancy, event handling, and UI components across 7 modules is unsustainable for a small team. The architecture must provide shared infrastructure without creating tight coupling.

D2: Multi-Tenancy as a First-Class Concept

"Multi-tenancy... is a first-class architectural concept, not an afterthought." — Spec Section 5

Every data row, every API request, and every UI view operates within a tenant context. The database enforces isolation via Row-Level Security. The application layer cannot bypass it.

D3: Enterprise-Grade Security (Progressive)

"Security is not a layer applied on top — it is embedded in every component." — Spec Section 5

The specification requires hybrid RBAC/ABAC, data classification enforcement, per-tenant encryption keys, HSM support, FIPS 140-2 compliance, and air-gapped deployment support. These cannot all be built on day one, but the architecture must not make them impossible.

D4: Deployment Flexibility

"The platform's architecture must support [on-premises] deployment without modification to the application logic." — Spec Section 7

The current prototype uses Supabase (hosted auth + DB), Vercel (hosting), and S3-compatible storage. The production platform must support self-hosted PostgreSQL, Keycloak (or equivalent), MinIO, and containerised deployment. Every infrastructure dependency must be behind a provider abstraction.

D5: AI-Optimised Development

The entire codebase will be developed and maintained by AI coding agents supervised by 1-2 humans. This imposes specific constraints:

  • Context window limits: Each module must fit within an AI agent's context window (~150 files)
  • Isolation: An AI agent working on one module must not be able to accidentally break another
  • Discoverability: Conventions, schemas, and contracts must be machine-readable
  • Reproducibility: Module scaffolding must be templated so new modules start consistently

D6: Small Team Reality

With 1-2 humans and AI agents, the architecture must minimise operational overhead:

  • Single repository (not 7+ repos to manage)
  • Single database instance (not 7 databases to operate)
  • Shared CI/CD pipeline (not per-module deployment infrastructure)
  • Minimal runtime dependencies (not a Kubernetes cluster with 30 services)

3. Module Rationalisation: From 13 to 7

The specification defines 13 modules (8 core + 5 extension). Analysis by domain boundaries, team capacity, and dependency structure leads to a rationalisation to 7 buildable modules.

Modules Retained as Independent Apps

#ModuleSpec SectionRationale
1Directory4.1Identity foundation — every module depends on it
2Catalog4.2 + 4.11Product/service listings + inventory availability (absorbed)
3Procurement4.3Core value proposition — RFQ, CPQ, scoring, orders
4Project Coordination4.4EXISTS as project-tracker — programme/stage-gate/task management. Also owns quality, helpdesk, and audit-tracker functionality (see below).
5Documentation4.5Secure document repository with classification
6Supply Chain4.10Multi-tier visibility, dependency tracing, risk indicators
7Compliance4.12Export control, sanctions screening, licence matching

Modules Absorbed or Deferred

Original ModuleSpec SectionDispositionRationale
E-Learning4.7Deferred — COTS integrationBuilding an LMS (SCORM, xAPI, proctoring) is a product in itself. Integrate a commercial LMS and expose completion data via API.
Communication & Notifications4.8Absorbed — cross-cutting packageNotifications, in-app messaging, and email are infrastructure, not a module. Implemented in packages/platform/notifications.
Self-Service Portal4.9Absorbed — UI layerThe portal is a supplier-facing view of Directory + Catalog + Procurement. It is a frontend route group, not a separate module.
Inventory Availability4.11Absorbed into CatalogStock levels and capacity data are attributes of catalog entries, not a separate bounded context.
Quality (Helpdesk + Quality + Audit Tracker)4.6 + 4.13Absorbed into Project CoordinationQuality/helpdesk shares the same entity model as project issues — separate modules would mean duplicate UIs, notification pipelines, and search indexes for the same concept. Tickets, findings, corrective actions, and certifications are modelled as issue types within the Project Coordination module.

Spec Section Cross-Reference Preserved

Every requirement from the original 13 modules is accounted for. No functionality is dropped — it is either assigned to one of the 7 modules, placed in a shared package, or explicitly deferred to COTS integration.


4. Monorepo Structure

The platform uses a Turborepo monorepo. All modules and shared packages live in a single repository, with strict boundaries enforced by ESLint and TypeScript project references.

4.1 Architectural Shape: Three Explicit Layers

Constellation adopts the same architectural discipline that proved strongest in the later Stella work: every module is designed around three explicit layers.

  1. Domain Core — deterministic business rules, repositories, policies, validation, and state transitions
  2. Tool Layer — high-level business capabilities that expose intent-oriented actions to UI flows, automation, and future AI assistants
  3. Automation Layer — workflows, jobs, event handlers, notifications, and bounded AI-assisted decisions that orchestrate the system without owning business truth

This is not a microservice split. It is an internal module design rule.

Rules:

  • The Domain Core is the source of truth for module state.
  • The Tool Layer exposes business capabilities such as publish_catalog_entry, build_rfq_shortlist, or schedule_audit, not thin CRUD wrappers.
  • The Automation Layer may orchestrate multi-step flows, but it never bypasses domain rules or writes directly to persistence without going through the Domain Core.
  • Any future agentic capability in Constellation must be introduced through the Tool Layer and Automation Layer, not by granting an LLM direct repository access.

4.2 Monorepo Topology

constellation/
├── apps/ # Target topology — entries marked ✓ exist today
│ ├── auth/ # Auth app — Login, registration, MFA, session management (Phase 3a, not yet built)
│ ├── directory/ # Module 1 — Identity, orgs, roles, tenancy ✓
│ ├── catalog/ # Module 2 — Products, services, taxonomy (Phase 2 skeleton, Phase 3 full)
│ ├── procurement/ # Module 3 — RFQ, CPQ, scoring, orders (Phase 3)
│ ├── project-tracker/ # Module 4 — Existing app, migrated into monorepo ✓
│ ├── documents/ # Module 5 — Document repo, classification (future)
│ ├── supply-chain/ # Module 6 — Multi-tier visibility (future)
│ └── compliance/ # Module 7 — Export control, sanctions (future)

├── packages/
│ ├── platform/ # Core shared infrastructure
│ │ ├── auth-core/ # Auth provider abstraction + permission primitives
│ │ ├── auth-next/ # Next.js auth adapter
│ │ ├── db/ # Prisma client factory, RLS, audit helpers
│ │ ├── events/ # Typed event bus + outbox
│ │ ├── jobs/ # Postgres-backed jobs, BullMQ adapter
│ │ ├── errors/ # Shared error hierarchy
│ │ └── testing/ # Fixtures, contract tests, integration harness
│ ├── ui/ # Design system: shadcn, Tailwind, shared components
│ ├── config/ # Shared tsconfig, ESLint, Vitest config
│ └── contracts/ # Shared Zod schemas, event contracts, classification types

├── tools/
│ └── create-module/ # Module scaffold generator (CLI tool)

├── docker/ # Docker Compose for local dev + self-hosted
│ ├── docker-compose.yml # PostgreSQL, (optional) Keycloak, MinIO
│ └── keycloak/ # Keycloak realm export for dev

├── docs/ # Architecture docs, ADRs, runbooks
│ └── adr/ # Architecture Decision Records

├── turbo.json # Turborepo pipeline config
├── package.json # Root workspace config
├── CLAUDE.md # Root AI agent context (conventions, rules)
└── .github/
└── workflows/ # Shared CI pipeline

4.3 Canonical Module Template

Each application follows a predictable internal shape so AI coding agents can discover the right place for changes without relying on framework magic.

apps/<module>/
├── src/
│ ├── app/ # Next.js routes, layouts, route handlers
│ ├── server/
│ │ ├── services/ # Domain Core business logic
│ │ ├── repositories/ # Data access only
│ │ ├── policies/ # Permission and classification rules
│ │ ├── tools/ # High-level business capabilities
│ │ ├── workflows/ # Automation Layer orchestration
│ │ └── events/ # Event publishers and handlers
│ ├── lib/ # Module-local utilities
│ └── components/ # Module-specific UI
├── prisma/
│ └── schema.prisma
├── tests/
│ ├── unit/
│ ├── integration/
│ └── property/
├── evals/ # Tool/workflow evaluations when automation is present
├── CLAUDE.md
└── README.md

4.4 Composition Model

Constellation avoids deep framework magic. Route handlers, server actions, jobs, and event handlers compose module behavior through explicit factory functions and shared platform helpers.

Rules:

  • Next.js is the delivery framework, not the owner of business composition.
  • Services, tools, and workflows are wired explicitly in module code.
  • No hidden DI container is required for platform primitives.
  • Any shared runtime bootstrap must remain plain TypeScript and framework-independent where possible.

4.5 Key Structural Rules

RuleEnforcement
Apps never import from other appsESLint boundaries plugin
Apps may only import from packages/TypeScript paths + ESLint
Each app has its own CLAUDE.mdConvention (checked in CI)
Each app has its own Prisma schemaPrisma multi-schema support
Shared packages are read-only for module AI agentsProcess discipline + branch protection
Module stays under 150 filesCI check (warn at 120, fail at 200)
Repositories never contain business decisionsCode review + module template
Tools and workflows call services, not raw PrismaCode review + module template

4.6 Technology Stack Per Module

Each module (app) is a Next.js application with:

LayerTechnologyNotes
FrameworkNext.js 16+ (App Router)Server components by default
LanguageTypeScript 5 (strict)Shared tsconfig from packages/tsconfig
DatabasePostgreSQL via Prisma 6Module-specific schema, shared DB instance
UITailwind CSS 4 + shadcn/uiShared design tokens from packages/ui
Auth@constellation-platform/auth-core + auth-nextProvider abstraction (Supabase / Keycloak / Mock)
ValidationZod 3.23+ (import { z } from 'zod')Shared base schemas where applicable
TestingVitest + fast-check + integration testsPlaywright optional for UI-heavy modules

5. Shared Platform Packages

Shared packages are the mechanism by which modules avoid code duplication while remaining independently deployable. Each package is versioned within the monorepo and consumed as a workspace dependency.

5.1 @constellation-platform/auth-core and @constellation-platform/auth-next

Purpose: Authentication and authorisation abstraction layer with a thin Next.js adapter.

Provider interface (extending the pattern already established in project-tracker):

AuthProvider
├── signIn(email, password?) → AuthResult
├── signOut() → void
├── getSession() → Session | null
├── signUp(email, password, metadata) → AuthResult
├── verifyMfa(code, challengeId) → AuthResult
├── enrollMfa(userId) → MfaEnrollment
├── resetPassword(email) → void
├── updatePassword(newPassword) → void
└── readonly name: string

Implementations:

  • MockAuthProvider — Local dev, no passwords, cookie-based
  • SupabaseAuthProvider — Current production (Supabase hosted auth)
  • KeycloakAuthProvider — Self-hosted / enterprise on-premises deployments (future)

Access control primitives (new, built for Directory module):

  • checkPermission(user, action, resource, context) → boolean
  • getTenantContext(request) → TenantContext
  • requireRole(role) → middleware
  • requirePermission(permission) → middleware

Key design decision: The auth packages provide primitives. They do NOT define the permission model — that is the Directory module's responsibility. The platform packages provide the evaluation engine and request-context wiring; the Directory module provides the policies, roles, and permissions data.

5.2 @constellation-platform/db

Purpose: Database connection management, Prisma client factory, RLS enforcement, and base model types.

Responsibilities:

  • Prisma client singleton with tenant-aware connection
  • RLS helper: SET LOCAL app.tenant_id = $1 on every transaction (via withTenantContext())
  • Connection pooling configuration (PgBouncer for serverless, direct for self-hosted)
  • Base model types (every table inherits id, tenantId, createdAt, updatedAt)
  • Shared audit capture helpers
  • Migration coordination across module schemas

What it does NOT do:

  • Define module-specific models (each module owns its own Prisma schema)
  • Enforce business rules (that's the module's job)

5.3 @constellation-platform/events

Purpose: Typed, append-only event bus for inter-module communication.

Canonical implementation strategy:

  • PostgreSQL outbox + LISTEN/NOTIFY is the default and canonical runtime model
  • durable subscriber state lives in PostgreSQL
  • BullMQ or another broker-backed path is allowed only for a queue that proves PostgreSQL throughput is insufficient

This is a deliberate simplification for a small team and an AI-assisted codebase. The architecture does not treat NATS, RabbitMQ, or Kafka as part of the baseline plan.

Event contract ownership:

Event definitions live in the module that publishes them (e.g., apps/directory/src/server/events/directory.events.ts). When another module needs to consume these events, the Zod payload schemas are exported to packages/contracts/ so consumers can validate payloads without importing from the publishing module.

# Each module owns its event definitions:
apps/directory/src/server/events/directory.events.ts # User.created, Org.verified
apps/project-tracker/src/server/events/projects.events.ts # Task.completed, Gate.reviewed

# Cross-module schemas (populated when first consumer is built):
packages/contracts/
├── directory.schemas.ts # Zod schemas for directory events consumed by other modules
├── projects.schemas.ts # Zod schemas for projects events consumed by other modules
└── ...

Note: Earlier drafts placed event contracts in packages/platform/events/contracts/. That location is superseded — packages/platform/events/ provides infrastructure only (outbox, dispatcher, subscriber), not domain-specific schemas.

Rules:

  • Event contracts are append-only — existing events are never modified
  • New fields may be added as optional — breaking changes require a new event version
  • Each event carries: id, eventType, tenantId, actorId, correlationId, payload, and createdAt (transport metadata materialised by the outbox as created_at, not a field modules must supply). Modules publish eventType, payload, and meta: { tenantId, actorId, correlationId } — the platform adds id and createdAt automatically
  • Consumers must be idempotent (events may be delivered more than once)

5.4 @constellation-platform/jobs

Purpose: Shared background execution abstraction for jobs and workflow wake-ups.

Canonical implementation strategy:

  • PostgreSQL-backed job queue by default
  • BullMQ adapter only for proven high-throughput cases
  • in-memory test adapter for unit tests

Rules:

  • Job payloads must carry tenantId, actorId?, and correlationId
  • job handlers are idempotent
  • retries and dead-letter behavior are platform-owned, not reimplemented per module

5.5 @constellation-platform/errors

Purpose: Shared error hierarchy and standard API error envelope.

Rules:

  • modules throw typed application errors
  • API routes serialize errors through one shared formatter
  • no module invents its own error envelope

5.6 @constellation-platform/testing

Purpose: Shared fixtures, auth token builders, contract tests, and integration harnesses.

Contents:

  • tenant-aware test fixtures
  • auth token builders
  • real-Postgres integration helpers
  • contract test helpers for platform packages and event schemas
  • scaffolding support for property tests and workflow/tool evals

5.7 packages/contracts, packages/config, and packages/ui

Purpose: Shared contracts, configuration, and UI primitives used by all modules.

Contracts

  • Zod schemas
  • event payload definitions
  • classification label types
  • shared API envelopes

Config

  • shared tsconfig
  • ESLint rules
  • Vitest + fast-check presets

UI

  • Tailwind CSS 4 configuration and design tokens
  • shadcn/ui primitives
  • Constellation-specific components
  • layout shells and form primitives with Zod integration

6. Database Architecture

Single Instance, Schema Separation

All modules share a single PostgreSQL instance. Each module owns a dedicated schema for its domain data. Cross-cutting concerns (tenancy, audit, events) live in platform schemas.

PostgreSQL Instance
├── public # Shared types, extensions (uuid-ossp, pgcrypto)
├── tenancy # Tenants, tenant_config, data_sharing_agreements
├── identity # Users, organisations, roles, permissions, credentials
├── catalog # Products, services, taxonomy, specifications
├── procurement # RFQs, offers, evaluations, orders
├── projects # Programmes, projects, stages, gates, tasks, tickets, findings, audits, certifications
├── documents # Documents, versions, access_records
├── supply_chain # Nodes, edges, risk_scores, forecasts
├── compliance # Licences, screening_results, rules
├── audit # Append-only audit entries (cross-cutting)
├── events # Event log + outbox (cross-cutting)
└── notifications # Notification queue + preferences (cross-cutting)

Row-Level Security Strategy

Every data table includes a tenant_id column (NOT NULL, with a foreign key to identity.tenants).

Note: Earlier drafts of this document referenced tenancy.tenants. That schema name is superseded — the tenant registry lives in the identity schema, managed by the Directory module. All implementations use identity.tenants.

RLS policy pattern:

-- Applied to every tenant-scoped table
ALTER TABLE catalog.products ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON catalog.products
USING (tenant_id::text = current_setting('app.tenant_id', true));

The @constellation-platform/db package sets app.tenant_id via SET LOCAL app.tenant_id = $1 at the start of every database transaction, derived from the authenticated request's tenant context.

Note: Earlier drafts of this document used app.current_tenant — that name is superseded. All implementations use app.tenant_id.

Cross-Module Data Access

Modules do NOT directly query each other's schemas. Instead:

  1. Read path: Modules call each other's APIs (HTTP) to read data they don't own. Example: Procurement reads product data from Catalog's API.
  2. Event path: Modules react to events from other modules. Example: Procurement subscribes to Project.milestoneReached events for payment triggers.
  3. Shared identity: All modules read from the identity schema for user/org/role resolution (mediated by @constellation-platform/auth-core).

Exception: The identity schema is readable (but not writable) by all modules, because user/org/role lookup is required on every authenticated request. This is the only cross-schema read permitted.

Migration Strategy

Each module manages its own Prisma migrations independently. The root turbo.json orchestrates migration order:

  1. packages/platform/db migrations (tenancy, audit, events schemas)
  2. apps/directory migrations (identity schema)
  3. All other module migrations (parallel, independent)

7. Identity, Tenancy and Access Control

This is the most critical architectural component. The specification (Section 6) demands a hybrid RBAC/ABAC model with configurable data classification, regional access controls, and programme-scoped roles. The existing project-tracker has a simple MANUFACTURER | CUSTOMER role string. The gap is large.

Tenancy Model

Tenant
├── id: UUID
├── name: string
├── slug: string (unique)
├── type: BUYER | SUPPLIER | PROGRAMME_OFFICE | PLATFORM_OPERATOR
├── parentTenantId: UUID? (for hierarchical tenants, e.g., division of a prime)
├── settings: JSON (classification scheme, allowed features, etc.)
└── status: ACTIVE | SUSPENDED | DECOMMISSIONED

Every request carries a tenant context. Users may belong to multiple tenants (e.g., a consultant working for two suppliers). Tenant switching is explicit (UI tenant selector), never implicit.

User and Organisation Model (Directory Module)

Organisation
├── id, tenantId, name, legalName, registrationNumber
├── type: BUYER | PRIME_CONTRACTOR | SUB_TIER_SUPPLIER | SME | PROGRAMME_OFFICE
├── parentOrganisationId (for modelling divisions/subsidiaries)
├── certifications[], securityClearances[], competenceDomains[]
└── status, verifiedAt, verifiedBy

User
├── id, tenantId, email, name, avatarUrl
├── organisationId (primary)
├── dataClassificationLevel: UNCLASSIFIED | RESTRICTED | CONFIDENTIAL | SECRET (tenant-configurable)
├── regionalCaveats: string[] (e.g., ["DEU", "FRA"] for region-restricted access)
├── status: ACTIVE | SUSPENDED | DEACTIVATED
└── federatedIdentityLinks[] (SAML, OIDC)

Role (scoped to tenant + optional programme/project)
├── id, tenantId, name, description
├── scopeType: GLOBAL | PROGRAMME | PROJECT | MODULE
├── scopeId: UUID? (programmeId or projectId, null for GLOBAL)
└── permissions[]

Permission
├── resource: string (e.g., "procurement.rfq", "documents.classified")
├── action: string (e.g., "create", "read", "update", "delete", "approve")
└── conditions: JSON? (ABAC conditions, e.g., {"clearance": ">=SECRET"})

Access Control Evaluation

canAccess(user, action, resource, context) → boolean

Where context includes:
- tenantId
- programmeId? (scoping)
- projectId? (scoping)
- resourceClassification? (for classification enforcement)
- resourceOwnerId? (for ownership checks)

Evaluation order:

  1. Tenant isolation — user must belong to the tenant (or have cross-tenant access grant)
  2. Role check (RBAC) — user must have a role that grants the action on the resource
  3. Attribute check (ABAC) — user's data classification level, regional caveats, and contextual attributes must satisfy the permission's conditions
  4. Classification enforcement — if the resource has a classification label, user must have sufficient access level

What the Project-Tracker Currently Has vs. What's Needed

AspectProject-Tracker (current)Directory Module (target)
User.roleSingle string: MANUFACTURER | CUSTOMERRole objects scoped to tenant/programme/project
PermissionsHardcoded if (user.role !== "MANUFACTURER")Dynamic permission evaluation via @constellation-platform/auth-core
TenancyNo tenant concepttenant_id on every row, RLS enforced
OrganisationsBasic Organisation model, optionalRequired, with hierarchy and qualification tracking
ClassificationsNonePer-user data classification levels and regional caveats
Identity federationNone (Supabase or mock only)SAML 2.0, OIDC via Keycloak or equivalent

Audit Trail Architecture

Enterprise procurement requires tamper-evident audit trails. Audit capture is part of @constellation-platform/db and available to all modules from Phase 1.

Audit Schema (audit schema, managed by platform)

audit_entries
├── id: UUID
├── tenant_id: UUID
├── actor_id: UUID (user who performed the action)
├── actor_type: USER | SYSTEM | AGENT (distinguishes human, automated, and AI actions)
├── action: string (e.g., "create", "update", "delete", "approve", "classify")
├── resource_type: string (e.g., "directory.organisation", "procurement.rfq")
├── resource_id: UUID
├── module: string (source module name)
├── changes: JSON (before/after diff for updates, null for creates/deletes)
├── classification: UNCLASSIFIED | RESTRICTED | CONFIDENTIAL | SECRET (label of the affected resource)
├── ip_address: string? (request origin, null for system/agent actions)
├── correlation_id: string (traces the request chain)
├── created_at: timestamptz
└── CONSTRAINT: No UPDATE or DELETE operations permitted on this table

Audit Rules

  1. Immutability: The audit_entries table is append-only. No UPDATE or DELETE permissions are granted to any application role. Database triggers reject mutation attempts.
  2. Automatic capture: All state-changing operations (create, update, delete, approve, status transitions) are audited via a shared auditAction() utility in @constellation-platform/db. Modules must not implement their own audit logic.
  3. Retention: Audit entries are retained indefinitely in the SaaS tier. Dedicated Cloud and On-Prem tiers allow configurable retention policies per tenant. Entries are never deleted — they are archived to cold storage after the retention window.
  4. Tamper evidence (Dedicated Cloud / On-Prem only): Audit entries include a chained hash (previous_hash + SHA-256(entry)) enabling integrity verification. The hash chain is validated nightly by a scheduled job. Breaks in the chain trigger an alert.
  5. Classification inheritance: Audit entries inherit the classification of the resource they describe. An audit entry for a SECRET document is itself SECRET.
  6. RLS enforcement: Audit entries are tenant-scoped. Users can only query audit entries for their own tenant. Platform operators can query cross-tenant for platform-level investigations.

Querying

Modules expose an audit endpoint via a shared API route pattern:

GET /api/audit?resource_type=procurement.rfq&resource_id=<uuid>&limit=50

The @constellation-platform/db package provides queryAuditTrail({ tenantId, resourceType?, resourceId?, actorId?, dateRange?, limit }) for programmatic access.


8. Event-Driven Integration

Integration Map (7 Modules)

Derived from Spec Section 8, rationalised to 7 modules:

Source ModuleEventConsumer Module(s)Integration
DirectoryUser.created, Org.verified, Role.assignedAll modulesIdentity context for all modules
DirectoryCredential.expired, Qualification.updatedProcurement, ComplianceSupplier eligibility changes
CatalogProduct.published, Product.updatedProcurementAvailable products for RFQs
CatalogProduct.deprecatedSupply Chain, ProjectsDependency impact notification
ProcurementRFQ.published, Offer.submittedDirectory (supplier activity scores)Activity tracking
ProcurementOrder.placedProjects, Documents, ComplianceTriggers project creation, contract storage, compliance check
ProjectsTask.completed, Stage.gateReviewedDocumentsDeliverable submission
ProjectsMilestone.reachedProcurement (milestone payments)Payment triggers
ProjectsTicket.created, Finding.resolvedDirectory (supplier quality scores)Supplier profile enrichment
ProjectsAudit.scheduled, CertExpiringDirectory, DocumentsCompliance tracking
DocumentsDocument.uploaded, Document.classifiedAudit, ComplianceAudit trail, classification enforcement
Supply ChainRiskScore.changed, Dependency.brokenProjects, ProcurementRisk-aware project management
ComplianceScreeningResult.flagged, Licence.expiredProcurement, ProjectsBlocking action on non-compliant items

Event Schema Standard

Every event follows this envelope:

interface ConstellationEvent<T> {
eventId: string; // UUID, globally unique
eventType: string; // e.g., "directory.user.created"
version: number; // Schema version (starts at 1)
tenantId: string; // UUID
actorId: string; // UUID of user who triggered the event
timestamp: string; // ISO 8601
correlationId?: string; // For tracing chains of events
payload: T; // Event-specific typed payload
}

9. Deployment Strategy and Enterprise Readiness

Three Deployment Tiers

The specification (Section 7) requires three deployment models. Rather than trying to build all three on day one, the architecture supports a progressive migration path.

Tier 1: SaaS Prototype (Current + Near-Term)

ComponentProviderPurpose
AuthSupabase AuthEmail/password, MFA
DatabaseSupabase PostgreSQL (EU)Managed, pooled via PgBouncer
File StorageSupabase StorageS3-compatible
HostingVercelServerless Next.js, EU region
EmailResendTransactional emails

Use case: Business plan validation, demo instances, low-classification SaaS customers.

Tier 2: Dedicated Cloud (Medium-Term)

ComponentProviderPurpose
AuthKeycloak on VM/containerFederation, SAML, OIDC
DatabaseSelf-managed PostgreSQLDedicated instance, full RLS
File StorageMinIO or cloud S3Dedicated buckets
HostingDocker Compose / K8sContainerised Next.js
EmailSMTP relayOrganisation-controlled

Use case: Dedicated customer instances, EU sovereign cloud (OVHcloud, T-Systems).

Tier 3: On-Premises / Air-Gapped (Long-Term)

ComponentProviderPurpose
AuthKeycloak with HSM integrationFIPS 140-2, CAC/PIV smart cards
DatabasePostgreSQL on bare metalCustomer-managed encryption keys
File StorageMinIO on bare metalEncrypted at rest, customer-managed keys
HostingK8s / Docker on-premAir-gapped, no external network
EmailInternal SMTP / disabledMay not be available in air-gapped

Use case: Regulated enterprise deployments (defence, government, critical infrastructure), classified environments.

Provider Abstraction Pattern

Every infrastructure dependency is accessed through an interface, never directly. The existing project-tracker's AUTH_PROVIDER pattern is the model:

// Configured via environment variable
const authProvider = createAuthProvider(process.env.AUTH_PROVIDER); // 'supabase' | 'keycloak' | 'mock'
const storageProvider = createStorageProvider(process.env.STORAGE_PROVIDER); // 'supabase' | 'minio' | 'local'
const emailProvider = createEmailProvider(process.env.EMAIL_PROVIDER); // 'resend' | 'smtp' | 'mock'

What Must Be True for Enterprise Readiness

These are not Phase 1 requirements, but the architecture must not make them impossible:

RequirementSpec SectionArchitecture Implication
RLS tenant isolation6tenant_id on every table from day one
Hybrid RBAC/ABAC6Permission evaluation engine in @constellation-platform/auth-core
Classification enforcement6Classification labels in shared contracts + module policies
Per-tenant encryption keys6KMS abstraction implemented behind platform packages
HSM support6Key management interface, not implementation
FIPS 140-26Use of standard crypto primitives, no custom cryptography
Air-gapped deployment7No build-time or runtime calls to external services
Identity federation4.1SAML 2.0 / OIDC via Keycloak provider
Append-only audit5Dedicated audit schema, no UPDATE/DELETE
EU data sovereignty2Deploy on EU sovereign cloud providers

10. AI-Assisted Development Model

This section governs both AI coding-agent ergonomics and future AI-assisted runtime features. Constellation is not a free-form agent runtime, but it is designed so bounded automation and later agentic workflows can be added safely.

Agent Architecture

The codebase is designed for development by AI coding agents (Claude, Copilot, or similar), supervised by human reviewers.

Agent Types

Agent RoleScopeAllowed to Modify
Platform Agentpackages/*Shared packages only
Module Agent (per module)apps/<module>/*One module only
CI/Infra Agent.github/, docker/, root configInfrastructure only

Rules for AI Agent Sessions

  1. One agent per module — An agent session NEVER modifies two apps simultaneously
  2. Shared packages are read-only for module agents — Only the Platform Agent modifies packages/
  3. Each app has its own CLAUDE.md — Contains domain context, data model, integration points, and conventions
  4. Module stays under 150 files — If a module exceeds this, it is a signal to refactor or split
  5. Event contracts are append-only — No agent may modify an existing event schema
  6. All PRs require human review — AI agents create PRs, humans approve them
  7. Tests are mandatory — No PR merges without passing unit + integration tests
  8. Tools before CRUD — agents implement business-capability tools and workflows before adding thin endpoint wrappers
  9. Automation cannot bypass the Domain Core — jobs, handlers, and AI-assisted flows call services/tools, never repositories directly

Context Document Standard

Every app's CLAUDE.md must contain:

# CLAUDE.md — [Module Name]

## Purpose

One-paragraph module description and its role in the platform.

## Tech Stack

Versions and key libraries.

## Commands

Dev, build, test, lint commands.

## Directory Structure

File tree with annotations.

## Data Model

Key entities and their relationships.

## API Routes

Endpoint inventory with auth requirements.

## Event Contracts

Events this module publishes and subscribes to.

## Tools

Business-capability tools exposed by this module.

## Workflows

Automation flows, jobs, approvals, and resumable processes.

## Evals

How tools/workflows are verified when automation or AI-assisted decisions are present.

## Integration Points

Which modules this one depends on and how.

## Conventions

Module-specific patterns and gotchas.

Module Scaffold Generator

tools/create-module provides a CLI that generates a new module with:

  • Next.js app boilerplate with App Router
  • Prisma schema with tenant_id base model
  • CLAUDE.md template
  • Auth middleware using @constellation-platform/auth-core and auth-next
  • Tool and workflow boilerplate
  • Event publisher/subscriber boilerplate
  • Test setup (Vitest + fast-check + integration harness)
  • Eval scaffold when the module includes automation features
  • CI integration
npx create-constellation-module --name catalog --port 3002

Code Quality Enforcement

CheckToolScope
Type safetyTypeScript strict modeAll packages and apps
LintingESLint with shared configAll packages and apps
Module boundarieseslint-plugin-boundariesCross-app import prevention
Test coverageVitest + fast-check + integration testsPer-module, min 80% for shared packages
File countCustom CI checkWarn at 120, fail at 200 per module
Event contract validationZod schema checkAll event contracts
CLAUDE.md presenceCI checkEvery app must have one
Tool/eval presenceCI check for automation modulesTools and workflows require matching evals

11. Existing Project-Tracker: Migration Strategy

The project-tracker (apps/project-tracker/) is a functioning Next.js application with:

  • 25 Prisma models (including Organisation, User, Role, RolePermission, UserRole, ProjectCollaborator, etc.)
  • Dual auth provider (Supabase + Mock) with MFA support
  • 48+ API routes
  • Programme/stage-gate/task management
  • Phases 1-2 complete, Phases 3-5 planned

Migration Approach: Strangler Fig Pattern

The project-tracker is NOT refactored upfront. Instead:

  1. Step 1 (Week 1): Move project-tracker into apps/project-tracker/ within the monorepo. It continues to use its own Prisma schema, its own auth, and its own database. No functional changes.

  2. Step 2 (After Directory module is built): The project-tracker begins consuming @constellation-platform/auth-core and @constellation-platform/auth-next for authentication, replacing its local src/lib/auth-providers/. The Directory module becomes the source of truth for users, orgs, and roles.

  3. Step 3 (After shared packages are proven across 2-3 modules): The project-tracker migrates its Prisma schema to use tenant_id, moves to the projects database schema, and adopts RLS. Its Organisation, User, Role, RolePermission, UserRole models are replaced by references to the identity schema.

  4. Step 4 (Ongoing): Project-tracker Phases 3-5 (polish, advanced features, production hardening) are built using the shared platform packages.

Risk Mitigation

  • The project-tracker remains fully functional at every step
  • Each migration step is a separate PR with rollback capability
  • The live deployment at constellation-project-tracker.vercel.app is unaffected until each step is validated

12. Relationship to Stella Catalog

Context

The team also develops Stella Catalog, an AI-first, open-source PIM system (NestJS, PostgreSQL + pgvector, originally designed for Open Mercato). The original architecture (v1.0) treated Stella as a separate product with a shared technology platform. After further analysis, the decision has been revised.

Decision: Stella Is Merged into the Constellation Catalog Module (Option 3)

Revised April 2026. Stella Catalog's full feature set is absorbed into Constellation's Catalog module (apps/catalog/). The Catalog module is rebuilt as a Next.js application that incorporates Stella's core capabilities natively, rather than maintaining two separate products.

What the Catalog Module Absorbs from Stella

Stella CapabilityConstellation Catalog Implementation
Semantic search (pgvector)Native vector search with embedding dirty-checking, integrated with RLS for tenant-scoped results
CPQ (Configure-Price-Quote)Product configuration and pricing engine, feeds directly into Procurement module
Supplier matchingAI-powered supplier-product matching, scoring, and recommendation within tenant context
Channel syndicationMarketplace and channel export (Amazon, Shopify, etc.) as optional tenant-level feature
AI-powered enrichmentAutomated product data enrichment, classification suggestion, and attribute completion
CanonicalProduct data modelAdapted into Constellation's product model with tenant isolation and configurable taxonomy support
SupplierOffer modelMerged into Catalog's supplier-product relationship, linked to Directory supplier profiles

Industry-Specific Features as Tenant Configuration

Features that were previously considered domain-specific are now configurable per tenant rather than hardcoded:

FeatureImplementation
Product taxonomyConfigurable industry taxonomy (LTREE). Options include NATO Supply Classification, CPV, UNSPSC, GPC, or custom tenant-defined taxonomies
Data classification labelsOptional per-tenant feature. Tenants in regulated industries (defence, government, healthcare) enable classification enforcement; commercial tenants may disable it
Access-filtered searchVector search + RLS. When classification is enabled, search results are filtered by user access level. When disabled, standard tenant-scoped RLS applies
Compliance metadataOptional fields for export control, origin tracking, and regulatory attributes — activated per tenant based on industry requirements

Migration Approach

The merger follows a phased approach:

  1. Phase 1: Constellation Catalog (apps/catalog/) is built as a Next.js app using Constellation's shared platform packages. Core product CRUD, taxonomy (LTREE), and tenant-scoped search are implemented natively.
  2. Phase 2: Stella's AI capabilities (semantic search, embedding dirty-checking, AI enrichment) are ported from NestJS to Next.js API routes, adapting Stella's PostgreSQL + pgvector patterns directly.
  3. Phase 3: Advanced features (CPQ, supplier matching, channel syndication) are added as optional tenant-level modules within the Catalog app. These features are enabled via tenant configuration, not separate deployments.

What Happens to the Stella Repository

  • Stella's open-source repository remains available as a standalone NestJS PIM for the broader community.
  • Constellation does not depend on or import from the Stella codebase at runtime.
  • Stella's patterns, data models, and AI techniques inform the Constellation Catalog implementation, but all code is rewritten for the Next.js / Constellation platform stack.
  • The Common_Technology_Platform_v1.md document is superseded by this merger for Constellation's purposes. Shared technology patterns are now simply part of the Constellation platform packages.

13. Patterns Adopted from Open Mercato

Context

The team also develops Open Mercato, an AI-supportive CRM/ERP foundation framework (Next.js, MikroORM, PostgreSQL, Redis, 14 shared packages, 20+ modules). A formal analysis evaluated 15 Open Mercato patterns for applicability to Constellation's enterprise commerce context.

See: Open_Mercato_Pattern_Analysis_v1.md for the full evaluation.

Adopted Patterns (8 of 15)

PatternAdaptation for Constellation
Event-Driven DecouplingTyped events with as const, domain-verb naming, transactional outbox (PostgreSQL), audit-critical events are synchronous
Module Setup Lifecyclesetup.ts per module with onTenantCreated, seedDefaults, defaultRoleFeatures. seedExamples hard-gated from production
No Cross-Module ORM RelationsFK IDs only across modules. Typed service contracts in packages/contracts. Prisma schema lint in CI
Spec-Driven Development.kiro/specs/ directory. Tasks with 3+ file changes require a spec before implementation
Field-Level EncryptionTenant-scoped DEKs, Prisma $extends for transparent encrypt/decrypt. HSM in production, local in dev. No deterministic hashing in regulated environments
CLAUDE.md Task RoutingRoot CLAUDE.md with task routing table. Per-module CLAUDE.md with domain context + import tables + known pitfalls
Module-Decoupling TestCI check: each module builds independently. Import boundary lint. Prisma schema cross-reference check
Import Pattern TablesEach module CLAUDE.md maps "need X → import from Y → NOT from Z"

Adapted Patterns (2 of 15)

PatternWhat Changed
CRUD Factory → Tool-Oriented CompositionEnterprise entities are too varied for a single factory. Use explicit services, tools, and workflows with composable policy/audit helpers instead
Dual Queue → Postgres-First RuntimePostgreSQL outbox + jobs are canonical. BullMQ is optional for high-throughput. In-memory adapter for unit tests

Skipped Patterns (5 of 15)

PatternWhy Skipped
Module Auto-DiscoveryNext.js App Router already provides conventions. 7 modules can be listed explicitly.
Awilix DI ContainerRuntime opacity hinders compliance auditing. React cache() + factory functions suffice.
Widget InjectionClassification boundaries could be violated. Use explicit imports + parallel routes.
Custom Entities / EAVRegulated schemas are compliance-driven and rigid. Use metadata Json? for narrow cases only.
Overlay/Override SystemEnterprise deployments need deterministic, auditable builds. Use feature flags instead.

14. Execution Roadmap

Phase 0: Monorepo Setup (1 week)

TaskDeliverable
Initialise Turborepo workspaceturbo.json, root package.json
Move project-tracker into apps/Working project-tracker in monorepo
Create packages/tsconfig and packages/eslint-configShared configs
Set up Docker Compose for local PostgreSQLdocker/docker-compose.yml
Create root CLAUDE.md with task routing table + universal rulesAI agent governance (Open Mercato pattern)
Create .kiro/specs/ directory with templateSpec-driven development (Open Mercato pattern)
Set up module-decoupling CI checkIsolation guardrail (Open Mercato pattern)
Set up GitHub Actions CI for monorepoLint, type-check, test across all apps

Phase 1: Directory Module + Platform Packages (6-8 weeks) — API Only

TaskDeliverable
Design Directory data model (Users, Orgs, Roles, Permissions, Tenants)Prisma schema for identity + tenancy schemas
Build @constellation-platform/auth-core and auth-nextAuth packages with permission evaluation engine
Build @constellation-platform/db with RLS + audit helpersDB package with tenant-aware Prisma client
Build Directory API (CRUD for users, orgs, roles, credentials)REST API with auth middleware
Build @constellation-platform/events with pg LISTEN/NOTIFYEvent bus with typed contracts
Build @constellation-platform/jobsPostgres-first job abstraction
Write Directory CLAUDE.mdAI agent context for the module

Note: Directory UI is deferred to Phase 3a (UI Layer). Phase 1 delivers the complete backend API and platform packages. packages/ui is built as part of Phase 3a when all core module backends are available.

Phase 2: Extract and Stabilise (2 weeks)

TaskDeliverable
Extract patterns from Directory into packagesStabilised shared packages
Build tools/create-module scaffold generatorCLI tool for new modules
Document shared package APIsPackage-level documentation
Validate shared packages by building a minimal Catalog skeletonProof that packages work for a second module

Phase 3: Catalog + Procurement (8-12 weeks)

TaskDeliverable
Build Catalog module (products, taxonomy, search, shortlisting)Full catalog application
Build Procurement module (RFQ, CPQ, scoring, orders)Full procurement application
Integrate Catalog → Procurement flow (shortlists become RFQs)End-to-end procurement workflow
Integrate Procurement → Projects (orders trigger project creation)Event-driven integration

Phase 3a: UI Layer (4-6 weeks)

Build the user-facing frontend for the core modules. This phase delivers the first end-to-end user experience and coincides with the Business Plan Phase 2 "Commercial Pilot Release" milestone.

TaskDeliverable
Build packages/ui — shared component library (shadcn, Tailwind, layouts)Design system package consumed by all module apps
Build apps/auth/ — login, registration, MFA, password reset, session mgmtStandalone auth app wrapping Supabase (or Keycloak)
Build Directory UI — org profiles, user management, role admin, certsFrontend in apps/directory/ consuming Directory API
Build Catalog UI — catalog entry creation, search, filter, shortlistingFrontend in apps/catalog/ consuming Catalog API
Build Procurement UI — RFQ creation from shortlists, offer submissionFrontend in apps/procurement/ consuming Procurement API
Implement shared navigation component across module appsCross-module nav via URL links (no micro-frontend)

Dependencies: Phase 1 (Directory API), Phase 3 (Catalog + Procurement APIs). This phase produces packages/ui/ as a deliverable.

Key decisions:

  • apps/auth/ is the central entry point for all authentication flows. Other modules redirect to apps/auth/ for login and rely on a shared auth cookie for session continuity.
  • Each module app owns its own UI routes. Shared components (nav, layouts, form primitives) live in packages/ui/.
  • Supplier self-service (org profile, catalog management, RFQ response) is a frontend route group within the existing module apps, not a separate application.

Phase 4: Project-Tracker Migration (2-3 weeks)

TaskDeliverable
Replace project-tracker auth with @constellation-platform/auth-core and auth-nextShared auth in project-tracker
Add tenant_id to project-tracker schemaTenant isolation
Complete project-tracker Phases 3-5 using shared packagesProduction-ready project coordination

Phase 5: Remaining Modules (8-13 weeks, parallel AI agents)

ModuleEstimated DurationDependencies
Documents4-5 weeksDirectory (auth), Security (classification)
Supply Chain3-4 weeksDirectory (auth), Catalog (product references)
Compliance3-4 weeksDirectory (auth), Procurement (screening integration)

Phase 6: Enterprise Readiness (Ongoing, parallel)

TaskTrigger
Keycloak auth provider implementationFirst customer requiring SAML/OIDC federation
MinIO storage providerFirst customer requiring on-prem file storage
Docker/K8s deployment manifestsFirst dedicated/on-prem deployment
HSM key management integrationFirst customer requiring FIPS 140-2
Full RLS audit and penetration testingBefore any regulated or classified data enters platform

Phase Crosswalk: Architecture vs Business Plan

The architecture roadmap and business plan use independent phase numbering. This table maps between them to avoid confusion.

Architecture PhaseBusiness Plan PhaseKey Deliverables
Phase 0: Platform BootstrapPhase 1 (Foundation) — infrastructureMonorepo, platform packages, scaffold generator
Phase 1: Directory ModulePhase 1 (Foundation) — first moduleIdentity, orgs, roles, tenancy backend API
Phase 2: Extract & StabilisePhase 1 (Foundation) — hardeningContracts package, package docs, Catalog skeleton
Phase 3: Catalog + ProcurementPhase 2 (Commercial Pilot) — backendsFull Catalog and Procurement backend APIs
Phase 3a: UI LayerPhase 2 (Commercial Pilot) — frontendspackages/ui, apps/auth/, module UIs, end-to-end UX
Phase 4: Project-Tracker MigrationPhase 2 (Commercial Pilot) — migrationProject-tracker on shared platform
Phase 5: Remaining ModulesPhase 3 (Operational Delivery)Documents, Supply Chain, Compliance
Phase 6: Enterprise ReadinessOngoingKeycloak, MinIO, K8s, HSM, penetration testing

Rule of thumb: Architecture phases are sequenced by technical dependency. Business plan phases are grouped by commercial milestone. When in doubt, the architecture spec governs build order; the business plan governs release scope.


15. Risk Register

R1: Premature Abstraction (HIGH)

Risk: Shared packages are designed based on assumptions from the project-tracker and the spec, but they don't fit the real needs of the Directory or subsequent modules. The abstractions are wrong and need repeated refactoring.

Mitigation: Build the Directory module FIRST. Extract shared packages FROM the Directory after it works. The packages are derived from reality, not speculation. Validate by building a second module (Catalog) before declaring packages stable.

R2: Schema Drift Across Modules (MEDIUM)

Risk: Different AI agent sessions build modules with inconsistent patterns: different error response formats, different naming conventions, different approaches to pagination and filtering.

Mitigation: The module scaffold generator (create-module) enforces structural consistency. The root CLAUDE.md defines binding conventions. ESLint rules enforce import boundaries. CI validates structural compliance.

R3: Enterprise Stack Incompatibility (HIGH)

Risk: The SaaS prototype (Supabase + Vercel) becomes so deeply embedded that migrating to self-hosted infrastructure requires a rewrite.

Mitigation: Provider abstractions from day one. No direct Supabase imports outside of SupabaseAuthProvider and SupabaseStorageProvider. No Vercel-specific features (like Vercel KV) in application code. Regular "self-hosted smoke tests" using Docker Compose.

R4: Scope Creep from AI Agents (MEDIUM)

Risk: AI agents, given a detailed spec section, over-engineer features. They build the full SCORM-compatible LMS instead of a stub that integrates with a COTS LMS. They implement a complete industry-standard document format parser when a simple classification label would suffice.

Mitigation: Each module's CLAUDE.md includes explicit scope boundaries: "Build these features. Do NOT build these features. These are deferred to Phase X." Human reviewers enforce scope during PR review.

R5: Single Database Bottleneck (LOW-MEDIUM)

Risk: A single PostgreSQL instance serving 7 modules becomes a performance bottleneck as data volume grows.

Mitigation: Schema separation means modules can be migrated to dedicated databases later with minimal application code changes (change the Prisma datasource URL). PostgreSQL's built-in partitioning, read replicas, and connection pooling provide significant headroom. This becomes a concern only at scale, not during initial development.

R6: Project-Tracker Migration Fails (MEDIUM)

Risk: The project-tracker's 25-model Prisma schema, 48 API routes, and hardcoded auth patterns resist clean migration to the shared platform. The migration takes longer than building a new module.

Mitigation: The Strangler Fig approach means migration is incremental and each step is reversible. In the worst case, the project-tracker remains as a "legacy" module with its own auth, and users access it through a different login flow. This is suboptimal but not catastrophic.

R7: Team Capacity vs. Module Count (HIGH)

Risk: Even with 7 modules instead of 13, the total scope (7 modules + shared packages + infrastructure + testing + deployment) exceeds what 1-2 humans + AI agents can deliver in a reasonable timeframe.

Mitigation: The phased roadmap prioritises the modules with the highest business value and the most dependencies (Directory, Catalog, Procurement). Modules 5-7 (Documents, Supply Chain, Compliance) are lower priority and can be deferred if necessary without blocking the core platform value.


16. Resolved Architecture Decisions (formerly Open Questions)

The following questions were resolved during the architecture review session (March 2026). Each decision is now locked and binding on implementation.

Q1: Monorepo Scope → Option B: Add modules as development begins

Decision: Only project-tracker and directory exist initially in apps/. New module directories are scaffolded from create-module when development begins.

Rationale: Empty scaffolds mislead about progress, consume AI agent context unnecessarily, and create merge conflicts on files nobody is editing. The create-module generator is a Phase 0 deliverable and must exist before Phase 1 begins.

Rule: A module directory MUST NOT be created until its design spec (.kiro/specs/<module>/design.md) is approved.

Q2: Database Architecture → Schema-per-module (single instance)

Decision: Schema-per-module within a single PostgreSQL instance. This was already recommended; now locked.

Rationale: 1-2 humans cannot operate 7 databases. Schema separation provides sufficient isolation. Migration to separate databases later requires only changing the Prisma datasource URL per module — the application code is unchanged.

Rule: Each module owns its schema. Cross-schema reads are forbidden in application code (exception: identity schema is shared read). Cross-module data access is via API or event-driven projections only.

Q3: Framework Strategy → Next.js for all Constellation modules

Decision: All Constellation modules use Next.js 16+ App Router. No mixed framework exceptions.

Rationale: Consistency eliminates framework-selection debates for each new module. The deployment model, auth middleware, and UI component library work identically everywhere. API-heavy modules (e.g., Compliance) use Next.js API routes — the overhead is negligible compared to the team productivity gain. If a module later proves that Next.js is a bottleneck, it can be extracted — but this is a future optimisation, not a starting assumption.

Rule: All apps/<module>/ directories use Next.js. No Fastify, Hono, or Express modules in the Constellation monorepo.

Q4: Keycloak Timing → Option B: Defer until customer requires it

Decision: Use Supabase Auth for all auth in Phase 0-3. Implement KeycloakAuthProvider only when a customer contract requires SAML 2.0 federation or on-premises identity management.

Rationale: Keycloak adds significant operational complexity (Java runtime, realm configuration, theme customisation). The provider abstraction ensures the swap is mechanical, not architectural. Building Keycloak support before a customer needs it is premature optimisation for a 1-2 person team.

Rule: No direct Supabase Auth imports outside of SupabaseAuthProvider. All auth goes through @constellation-platform/auth-core and @constellation-platform/auth-next. This makes the Keycloak swap a single provider implementation, not a codebase refactor.

Decision: Each module is a fully independent Next.js application with its own URL prefix. Shared authentication via cookie/session. No micro-frontend shell. A dedicated apps/auth/ application handles all authentication flows (login, registration, MFA, password reset, session management).

Rationale: Independent apps are dramatically simpler for AI agents to reason about (one module = one context). A shared shell introduces runtime complexity (module federation, shared state, routing conflicts) that is disproportionate for a small team. Users navigate between modules via a shared navigation component (packages/ui) that renders links to sibling module URLs. Authentication is separated into its own app because every module depends on it, and it should not be coupled to any single module's deployment lifecycle.

Rules:

  • No module may import from another module's src/. Cross-module navigation is via URL links only. Shared UI components live in packages/ui/.
  • All login/registration/MFA flows live in apps/auth/. Module apps redirect to apps/auth/ for unauthenticated users.
  • apps/auth/ consumes @constellation-platform/auth-core and @constellation-platform/auth-next. It does NOT own user/org/role data — that remains in the Directory module.
  • Temporary exception (project-tracker migration): apps/auth/ does not exist yet (Phase 3a). Until it is built, modules that are migrated into the monorepo (starting with project-tracker) retain their own /api/auth/* routes for login, logout, register, and MFA. When apps/auth/ is built, these module-local auth routes are removed and replaced with redirects to apps/auth/. This exception is tracked in the project-tracker migration spec.

Q6: Air-Gapped Development Toolchain → Deferred to Phase 6 (Enterprise Readiness)

Decision: The development toolchain remains cloud-connected (npm registry, GitHub Actions CI, cloud AI assistants) for Phase 0-5. Air-gapped toolchain requirements are addressed in Phase 6 when a customer contract requires it.

Rationale: Vendoring node_modules, self-hosting CI, and running local-only AI agents adds weeks of infrastructure work that delivers zero product value. The development team currently operates in unclassified environments. Air-gapped requirements apply to the deployment environment, not the development environment, until classified data enters the development workflow.

Rule: No air-gapped toolchain work before Phase 6. Document all external dependencies (npm packages, CI services, AI APIs) in a dependency manifest to prepare for eventual vendoring.


This document was reviewed, challenged, and revised in March 2026. All questions are resolved. Implementation may proceed following the phased roadmap in §14.