Complete Next.js 16 application with 13 fully implemented modules: Email Signature, Word XML Generator, Registratura, Dashboard, Tag Manager, IT Inventory, Address Book, Password Vault, Mini Utilities, Prompt Generator, Digital Signatures, Word Templates, and AI Chat. Includes core platform systems (module registry, feature flags, storage abstraction, i18n, theming, auth stub, tagging), 16 technical documentation files, Docker deployment config, and legacy HTML tool reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
32 KiB
ArchiTools — System Architecture
1. Platform Overview
ArchiTools is a modular internal web dashboard platform built for a group of architecture, urban design, and engineering companies based in Cluj-Napoca, Romania:
- Beletage SRL — architecture office
- Urban Switch SRL — architecture and urban projects
- Studii de Teren SRL — engineering, surveying, GIS, technical studies
The platform centralizes daily operational tools: document registries, generators, templates, inventories, AI-assisted workflows, and technical utilities. It replaces scattered standalone HTML tools and manual processes with a unified, themeable, module-driven dashboard.
Key constraints:
- Internal-first deployment (external/guest access planned for later phases)
- Romanian UI labels; English code and comments
- On-premise Docker deployment behind reverse proxy
- localStorage as initial persistence layer, abstracted for future database/object store migration
- Must function as a module platform, not a monolithic application
2. High-Level Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ NGINX PROXY MANAGER │
│ (TLS termination, routing) │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ DOCKER CONTAINER (Next.js) │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ PRESENTATION LAYER │ │
│ │ App Shell │ Sidebar │ Theme │ i18n │ Module Routes │ │
│ └──────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────┴────────────────────────────────────┐ │
│ │ MODULE LAYER │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Registra- │ │ Email │ │ Word XML │ │ Prompt │ ... │ │
│ │ │ tura │ │Signature │ │Generator │ │Generator │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
│ │ │ │ │ │ │ │
│ └───────┼─────────────┼────────────┼─────────────┼──────────────┘ │
│ │ │ │ │ │
│ ┌───────┴─────────────┴────────────┴─────────────┴──────────────┐ │
│ │ CORE SERVICES LAYER │ │
│ │ │ │
│ │ Module Registry │ Feature Flags │ Storage Abstraction │ │
│ │ Tagging System │ i18n Engine │ Theme Provider │ │
│ │ Auth Stub │ Navigation │ Config │ │
│ └──────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────┴────────────────────────────────────┐ │
│ │ STORAGE LAYER │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ localStorage │ │ MinIO │ │ Database │ │ │
│ │ │ (current) │ │ (planned) │ │ (planned) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Authentik │ │ MinIO │ │ N8N │
│ (SSO) │ │ (obj store) │ │ (automation) │
└─────────────┘ └──────────────┘ └──────────────┘
3. Core Architecture Principles
3.1 Module Platform, Not Monolith
The application is a platform that hosts modules. The shell (layout, sidebar, theme, navigation) exists to load and present modules. No module is assumed to exist at build time — the system must function with zero modules enabled.
3.2 Module Isolation
Each module owns its:
- Route subtree (
/app/(modules)/[module-name]/) - Business logic (
/modules/[module-name]/) - Types, services, hooks, and components
- Storage namespace (scoped key prefix)
- Configuration entry in the module registry
Modules must never import from another module's internal directories. Cross-module communication happens exclusively through core services (tagging system, storage abstraction, shared hooks).
3.3 Removability
Disabling a module via feature flag must not produce build errors, runtime errors, or broken navigation. This is enforced by:
- Config-driven navigation (only enabled modules appear)
- Dynamic imports for module routes
- No direct cross-module imports
- Feature flag guards at route and component boundaries
3.4 Storage Independence
No module or component may call localStorage, sessionStorage, or any browser storage API directly. All persistence flows through the storage abstraction layer, which resolves to the active adapter at runtime.
3.5 Presentation/Logic Separation
UI components receive data via props and hooks. Business logic lives in services/ (pure functions) and hooks/ (stateful logic). Components do not contain data transformation, validation, or persistence logic.
4. Layer Architecture
┌─────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ Next.js App Router pages, layouts, UI components │
│ shadcn/ui primitives, Tailwind styling │
│ Theme provider, i18n labels │
├─────────────────────────────────────────────────────┤
│ MODULE LAYER │
│ Module-specific components, hooks, services │
│ Module config, types, route pages │
│ Isolated per module, lazy-loadable │
├─────────────────────────────────────────────────────┤
│ CORE SERVICES LAYER │
│ Module registry, feature flags, navigation │
│ Storage abstraction, tagging, auth stub │
│ i18n engine, theme system, config │
├─────────────────────────────────────────────────────┤
│ STORAGE / INTEGRATION LAYER │
│ Storage adapters (localStorage, MinIO, DB, API) │
│ External service clients │
│ Environment configuration │
└─────────────────────────────────────────────────────┘
Layer Rules
| Layer | May Import From | Must Not Import From |
|---|---|---|
| Presentation | Module Layer, Core Services, Shared | — |
| Module Layer | Core Services, Shared | Other modules |
| Core Services | Shared, Config | Module Layer, Presentation |
| Storage/Integration | Config only | Everything else |
5. Runtime Architecture
5.1 Next.js App Router
The application uses Next.js 15 with the App Router. The routing structure:
app/
├── layout.tsx → Root layout: providers, shell wrapper
├── page.tsx → Dashboard home (widget grid)
├── globals.css → Tailwind base + custom tokens
└── (modules)/ → Route group (no URL segment)
├── registratura/
│ ├── page.tsx → Module entry page
│ └── [id]/
│ └── page.tsx → Detail view
├── email-signature/
│ └── page.tsx
└── ...
5.2 Client-Side Primary, SSR Where Needed
The platform is primarily a client-side interactive application. Most module pages use "use client" directives because they:
- Manage complex form state
- Interact with browser storage
- Require immediate user feedback
- Handle drag-and-drop, clipboard, and other browser APIs
Server-side rendering is used for:
- The shell layout (static structure, fast first paint)
- Metadata generation
- Any future API routes serving data to external consumers
5.3 Provider Stack
The root layout wraps the application in a provider stack:
<ThemeProvider>
<I18nProvider>
<StorageProvider>
<FeatureFlagProvider>
<AppShell>
{children}
</AppShell>
</FeatureFlagProvider>
</StorageProvider>
</I18nProvider>
</ThemeProvider>
Each provider is independent and can be replaced or extended without affecting others.
6. Module Isolation Model
6.1 Module Structure
Every module follows a standard directory structure:
src/modules/[module-name]/
├── components/ # Module-specific React components
├── hooks/ # Module-specific React hooks
├── services/ # Pure business logic functions
├── types.ts # TypeScript interfaces and types
├── config.ts # Module metadata for the registry
└── index.ts # Public API barrel export
6.2 Module Registration
Each module exports a ModuleConfig object:
interface ModuleConfig {
id: string; // Unique identifier (kebab-case)
name: string; // Romanian display name
description: string; // Romanian description
icon: string; // Lucide icon name
route: string; // Base route path
category: ModuleCategory; // Grouping for navigation
enabled: boolean; // Default enabled state
featureFlag: string; // Flag key in feature flag system
requiredRole?: UserRole; // Minimum role (future use)
visibility?: Visibility; // internal | admin | public
version: string; // Semver
storageNamespace: string; // Storage key prefix
}
The central module registry (src/config/modules.ts) imports all module configs and provides them to the navigation system and feature flag guards.
6.3 Module Lifecycle
1. Module config registered in modules.ts
2. Feature flag checked at runtime
3. If enabled: route is accessible, nav item visible
4. Module page loads (dynamic import possible)
5. Module initializes its hooks/services
6. Module reads/writes through storage abstraction
7. If disabled: route returns redirect/404, nav item hidden
7. Core Systems Overview
7.1 Module Registry
Location: src/core/module-registry/
Central catalog of all available modules. Provides:
- List of all registered modules with metadata
- Lookup by ID, route, or category
- Filtering by enabled state, visibility, role
- Module category grouping for navigation
The registry is the single source of truth for what modules exist. Navigation, routing guards, and the dashboard widget grid all read from it.
7.2 Feature Flags
Location: src/core/feature-flags/
Controls module activation and experimental feature visibility.
interface FeatureFlag {
key: string;
enabled: boolean;
scope: 'module' | 'feature' | 'experiment';
requiredRole?: UserRole;
description: string;
}
Flag resolution order:
- Environment variable override (
NEXT_PUBLIC_FLAG_*) - Runtime config (
src/config/flags.ts) - Default from module config
Flags are checked via the useFeatureFlag(key) hook and the <FeatureGate flag="key"> component wrapper.
7.3 Storage Abstraction
Location: src/core/storage/
All data persistence flows through a StorageService interface:
interface StorageService {
get<T>(namespace: string, key: string): Promise<T | null>;
set<T>(namespace: string, key: string, value: T): Promise<void>;
delete(namespace: string, key: string): Promise<void>;
list(namespace: string): Promise<string[]>;
clear(namespace: string): Promise<void>;
}
Adapters:
| Adapter | Status | Use Case |
|---|---|---|
LocalStorageAdapter |
Current | Browser-local persistence, demo/dev mode |
MinIOAdapter |
Planned | File and object storage (signatures, templates) |
DatabaseAdapter |
Planned | Structured data (registry entries, inventory) |
APIAdapter |
Planned | External service delegation |
The active adapter is resolved at startup from environment configuration. Modules never know which adapter is active.
Namespace isolation: Each module operates within its own namespace (e.g., registratura:entries, password-vault:credentials). Modules cannot read or write to another module's namespace without explicit cross-module service mediation.
7.4 Tagging System
Location: src/core/tagging/
A cross-module tagging service used by multiple modules to categorize and link entities.
Tags are structured objects:
interface Tag {
id: string;
label: string; // Romanian display label
category: TagCategory; // project | client | domain | custom
color?: string;
metadata?: Record<string, unknown>;
createdAt: string;
}
The tagging system provides:
- Tag CRUD operations (stored via storage abstraction)
- Tag selector component (shared UI)
- Tag filtering and search
- Tag usage tracking across modules
Modules that use tags: Registratura, Prompt Generator, Word Templates, Digital Signatures, IT Inventory, Address Book.
7.5 Internationalization (i18n)
Location: src/core/i18n/
Current implementation: Romanian-only with structured label access for future multi-language support.
Labels are organized by module namespace:
const labels = {
common: {
save: 'Salvează',
cancel: 'Anulează',
delete: 'Șterge',
search: 'Caută',
// ...
},
registratura: {
title: 'Registratură',
newEntry: 'Înregistrare nouă',
// ...
},
};
Access pattern: useLabel('registratura.newEntry') or <Label k="common.save" />.
The system is designed so that adding a second language requires only adding translation files, not changing component code.
7.6 Theme System
Location: src/core/theme/
Dark/light theme support using CSS custom properties and Tailwind's dark: variant.
Design tokens:
- Background and surface colors
- Text hierarchy (primary, secondary, muted)
- Border and divider colors
- Accent colors per company (Beletage, Urban Switch, Studii de Teren)
- Semantic colors (success, warning, error, info)
Theme preference is persisted in storage and respects system preference as default. The theme provider exposes useTheme() with theme, setTheme, and toggleTheme.
Visual style: professional, technical, card-based dashboard. No playful or consumer-oriented aesthetics.
7.7 Auth Stub
Location: src/core/auth/
Current state: no authentication enforced (internal network only).
The auth module provides a stub interface that modules can code against:
interface AuthContext {
user: User | null;
role: UserRole; // 'admin' | 'user' | 'guest'
isAuthenticated: boolean;
company: CompanyId | null;
permissions: string[];
}
In the current phase, AuthContext returns a default internal user with admin role. When Authentik SSO integration is implemented, the auth module will resolve real user identity from SSO tokens without any module code changes.
Data model fields (visibility, requiredRole, createdBy) are included from day one so that enabling auth does not require data migration.
8. External Integration Points
8.1 Current Infrastructure
ArchiTools runs alongside existing services on the internal network:
| Service | Integration Type | Purpose |
|---|---|---|
| Authentik | Future SSO provider | User authentication and role assignment |
| MinIO | Future storage adapter | Object/file storage for documents, signatures, templates |
| N8N | Future webhook/API | Workflow automation (document processing, notifications) |
| Gitea | Development | Source code hosting |
| Stirling PDF | Dashboard link | PDF manipulation (external tool link) |
| IT-Tools | Dashboard link | Technical utilities (external tool link) |
| Filebrowser | Dashboard link | File management (external tool link) |
| Uptime Kuma | Dashboard widget | Service health status |
| Netdata | Dashboard widget | Server performance metrics |
8.2 Integration Patterns
Dashboard links: External tools appear as navigation entries or dashboard widgets with target="_blank" links. No embedding or API integration needed.
Dashboard widgets: Services like Uptime Kuma and Netdata can expose status endpoints or embed iframes for health/monitoring widgets on the dashboard home.
Storage integration (MinIO): When the MinIO adapter is implemented, modules that manage files (Digital Signatures, Word Templates) will store binary assets in MinIO buckets while keeping metadata in the primary storage.
Automation integration (N8N): Modules can trigger N8N webhooks for automated workflows. Example: Registratura creates a new entry, triggering an N8N workflow that sends a notification or generates a document.
SSO integration (Authentik): The auth stub will be replaced with an Authentik OIDC client. The middleware layer will validate tokens and populate AuthContext. No module code changes required.
9. Data Flow Patterns
9.1 Module Data Read
User action
→ Component calls hook (e.g., useRegistryEntries())
→ Hook calls service function (e.g., getEntries())
→ Service calls StorageService.get(namespace, key)
→ StorageService resolves active adapter
→ Adapter reads from storage backend
→ Data returns up the chain
→ Hook updates state
→ Component re-renders
9.2 Module Data Write
User submits form
→ Component calls hook mutation (e.g., createEntry(data))
→ Hook validates via service (e.g., validateEntry(data))
→ Service calls StorageService.set(namespace, key, data)
→ Adapter writes to storage backend
→ Hook updates local state / invalidates cache
→ Component re-renders with new data
9.3 Cross-Module Data (via Tagging)
User tags an entity in Module A
→ Module A calls TaggingService.addTag(entityId, tagId)
→ Tag association stored in tagging namespace
User filters by tag in Module B
→ Module B calls TaggingService.getEntitiesByTag(tagId)
→ Returns entity IDs across modules
→ Module B fetches its own entities matching those IDs
9.4 Feature Flag Check
Route or component renders
→ <FeatureGate flag="module.registratura">
→ useFeatureFlag('module.registratura')
→ Checks env override → config → default
→ Returns boolean
→ Children render or fallback shown
10. Deployment Architecture
10.1 Container Structure
┌──────────────────────────────────────────────────┐
│ Ubuntu Server (on-premise) │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Docker (via Portainer) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ ArchiTools │ │ Other containers │ │ │
│ │ │ (Next.js) │ │ (Authentik, MinIO, │ │ │
│ │ │ Port: 3000 │ │ N8N, Gitea, etc.) │ │ │
│ │ └──────┬───────┘ └──────────────────────┘ │ │
│ │ │ │ │
│ └─────────┼────────────────────────────────────┘ │
│ │ │
│ ┌─────────┴─────────────────────────────────────┐ │
│ │ Nginx Proxy Manager │ │
│ │ tools.internal.domain → localhost:3000 │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
10.2 Docker Configuration
Dockerfile: Multi-stage build.
- deps stage — installs Node.js dependencies
- build stage — runs
next build, produces standalone output - runtime stage — minimal Node.js image, copies standalone build, exposes port 3000
docker-compose.yml: Single service definition for ArchiTools. Environment variables passed from .env file. Optional volume mounts for persistent data if needed beyond localStorage.
10.3 Environment Variables
# Application
NEXT_PUBLIC_APP_URL=https://tools.internal.domain
NEXT_PUBLIC_APP_ENV=production
# Feature flags (override defaults)
NEXT_PUBLIC_FLAG_MODULE_REGISTRATURA=true
NEXT_PUBLIC_FLAG_MODULE_AI_CHAT=false
# Future: Storage backend
STORAGE_BACKEND=localStorage
MINIO_ENDPOINT=minio.internal.domain
MINIO_ACCESS_KEY=...
MINIO_SECRET_KEY=...
# Future: Auth
AUTHENTIK_ISSUER=https://auth.internal.domain
AUTHENTIK_CLIENT_ID=...
AUTHENTIK_CLIENT_SECRET=...
10.4 Build and Deploy Flow
Developer pushes to Gitea
→ (future: CI pipeline builds image)
→ Docker image built (manual or Watchtower auto-update)
→ Portainer deploys/restarts container
→ Nginx Proxy Manager routes traffic
→ Users access via internal domain
11. Scalability Considerations
11.1 Current Scale
- Users: ~5–20 internal staff across three companies
- Data volume: Low (hundreds to low thousands of records per module)
- Concurrency: Minimal (localStorage is per-browser, no shared state conflicts)
11.2 Growth Path
| Concern | Current | Growth Path |
|---|---|---|
| Data persistence | localStorage (per-browser) | Database + MinIO (shared, centralized) |
| Authentication | None (network trust) | Authentik SSO with RBAC |
| Multi-user data | Isolated per browser | Centralized with user ownership |
| File storage | Not supported | MinIO buckets per module |
| Search | Client-side filter | Server-side indexed search |
| API access | None | Next.js API routes for external consumers |
| Automation | Manual | N8N webhooks triggered by module events |
11.3 Module Scaling
New modules are added by:
- Creating the module directory structure
- Registering the module config
- Adding the feature flag
- Creating the route pages
No changes to the shell, navigation, or other modules are required. The navigation rebuilds itself from the registry.
12. Security Boundaries
12.1 Current Phase: Internal Network Trust
┌─────────────────────────────────────────────┐
│ Internal Network │
│ │
│ ┌──────────┐ ┌──────────────────────┐ │
│ │ Users │────▶│ ArchiTools │ │
│ │ (trusted) │ │ (no auth required) │ │
│ └──────────┘ └──────────────────────┘ │
│ │
│ Security: network-level only │
│ Data: browser-local, no shared secrets │
│ Risk: low (internal, trusted users) │
└─────────────────────────────────────────────┘
Current security model:
- Network perimeter security via Crowdsec and firewall rules
- No application-level authentication
- No sensitive data in localStorage (password vault uses demo-grade encryption)
- No external API endpoints exposed
- All data stays in the user's browser
12.2 Future Phase: SSO + Role-Based Access
┌──────────────────────────────────────────────────────────┐
│ │
│ ┌──────────┐ ┌────────────┐ ┌──────────────────┐│
│ │ Users │───▶│ Authentik │───▶│ ArchiTools ││
│ │(internal/ │ │ (SSO) │ │ (auth enforced) ││
│ │ external) │ └────────────┘ └──────────────────┘│
│ └──────────┘ │
│ │
│ Security: SSO + RBAC + module permissions │
│ Data: centralized DB + MinIO with access control │
│ Roles: admin, user, guest │
│ Visibility: per-field, per-module, per-company │
└──────────────────────────────────────────────────────────┘
Planned security layers:
- Authentik OIDC authentication (SSO)
- Role-based module access (admin, user, guest)
- Company-scoped data visibility
- Per-field visibility metadata (internal, admin, public)
- API route protection via middleware token validation
- Audit logging for sensitive operations
Design-for-security decisions made now:
- All data models include
visibilityandcreatedByfields - Module configs include
requiredRolefield - Feature flags support role-based activation
- Auth context interface defined (stubbed with defaults)
- Storage namespace isolation prevents cross-module data leaks
Appendix A: Technology Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Framework | Next.js 15 (App Router) | Modern React with file-based routing, SSR capability, API routes |
| Language | TypeScript | Type safety across modules, better refactorability |
| Styling | Tailwind CSS | Utility-first, consistent with shadcn/ui, theme support |
| Component library | shadcn/ui | Copy-paste components, full control, professional aesthetic |
| Deployment | Docker | Consistent with existing infrastructure (Portainer) |
| Initial storage | localStorage | Zero infrastructure, immediate development start |
| Storage pattern | Adapter abstraction | Allows migration without module changes |
| Auth pattern | Stub with interface | Enables SSO integration without refactoring |
Appendix B: Module Catalog
| Module | ID | Category | Status |
|---|---|---|---|
| Dashboard | dashboard |
Core | Planned |
| Registratura | registratura |
Registry | Planned |
| Email Signature Generator | email-signature |
Generators | Planned (legacy exists) |
| Word XML Generators | word-xml |
Generators | Planned (legacy exists) |
| Digital Signatures & Stamps | digital-signatures |
Assets | Planned |
| Password Vault | password-vault |
Security | Planned |
| IT Inventory | it-inventory |
Infrastructure | Planned |
| Address Book | address-book |
Contacts | Planned |
| Prompt Generator | prompt-generator |
AI Tools | Planned |
| Word Template Library | word-templates |
Templates | Planned |
| Tag Manager | tag-manager |
Administration | Planned |
| Mini Utilities | mini-utilities |
Tools | Planned |
| AI Chat | ai-chat |
AI Tools | Planned |