Resolve the operational/config sprawl (#10-#14 from the review): the app read NORMOGEN_*/MONGODB_* env vars but every env/compose file set SERVER_*/DATABASE_*, the ports were all over the place (8080/8000/6500/6800), there were 5 inconsistent Dockerfiles (rust:1.82 vs rust:1.93, missing curl), and an 18 MB binary was committed. Env-var names — standardize on what the code reads: * config/mod.rs: NORMOGEN_PORT default 8080 -> 6500 (avoid the over-common 8000/8080). * db/mod.rs: create_database() now reads MONGODB_DATABASE (was DATABASE_NAME). * .env.example, defaults.env, docker-compose.yml, docker-compose.dev.yml, DEPLOYMENT_GUIDE.md, deployment/README.md, deploy-and-test-solaria.sh, deploy-local-build.sh: use NORMOGEN_HOST/NORMOGEN_PORT/MONGODB_URI/ MONGODB_DATABASE/APP_ENVIRONMENT; drop the dead SERVER_*/DATABASE_URI/ DATABASE_NAME names. Ports — canonical container port 6500 everywhere: * Both Dockerfiles EXPOSE 6500; prod compose maps 6500:6500, dev 6501:6500. * Bulk-replaced the long tail of solaria:8000/localhost:8000/localhost:8080 in docs and test scripts -> 6500. Dockerfiles — 2 canonical, rust:latest, curl + healthcheck: * backend/Dockerfile (prod): rust:latest builder, debian runtime now installs curl (so the compose HEALTHCHECK actually works), EXPOSE 6500. * backend/docker/Dockerfile.dev (dev): rust:latest both stages, EXPOSE 6500. * Deleted 3 redundant Dockerfiles (Dockerfile.improved x2, docker/Dockerfile). * Deleted the committed 18 MB binary backend/docker/normogen-backend. * Deleted 2 stray fix-notes in backend/docker/. Compose: * docker-compose.yml: correct env names, 6500:6500, APP_ENVIRONMENT=production, JWT_SECRET/ENCRYPTION_KEY required via compose interpolation, dropped the obsolete top-level version: key. * docker-compose.dev.yml: correct env names, 6501:6500, mongo:7 (was 6.0), added a working backend healthcheck. * Deleted docker/docker-compose.improved.yml + backend/deploy-to-solaria-improved.sh (built around the now-deleted 'improved' Docker files). Verified: cargo fmt --check clean, build + clippy --all-targets clean, 18 unit tests pass; grep confirms no SERVER_*/DATABASE_* env names and no rust:1.x tags remain outside docs/archive and docs/adr (historical).
16 KiB
AI Agent Guide - Normogen Repository
Last Updated: 2026-03-09
Project: Normogen - Open-source health data platform
Repository Type: Monorepo (Rust backend + React frontend + Mobile placeholder)
🤖 Quick Start for AI Agents
1-minute Overview
- Backend: Rust (Axum web framework, MongoDB database)
- Frontend: React (TypeScript, Material-UI, Zustand state)
- Mobile: Placeholder (not yet implemented)
- Documentation: Organized in
docs/directory - Current Phase: 2.8 (Drug Interactions & Advanced Features)
Essential Commands
# Backend
cd backend && cargo build # Build backend
cd backend && cargo test # Run tests
cd backend && cargo clippy # Lint
cd backend && docker compose up -d # Run with Docker
# Frontend
cd web/normogen-web && npm install # Install dependencies
cd web/normogen-web && npm start # Dev server
cd web/normogen-web && npm test # Run tests
# Testing
./docs/testing/quick-test.sh # Quick smoke tests
./docs/testing/test-api-endpoints.sh # API tests
📁 Repository Structure
normogen/
├── backend/ # Rust backend (Axum + MongoDB)
│ ├── src/
│ │ ├── handlers/ # API route handlers (auth, users, medications, etc.)
│ │ ├── models/ # Data models & repositories
│ │ ├── auth/ # JWT authentication
│ │ ├── security/ # Rate limiting, audit logging, session management
│ │ ├── middleware/ # Custom middleware (JWT auth, rate limiting)
│ │ ├── services/ # Business logic (OpenFDA, interactions)
│ │ ├── config/ # Configuration management
│ │ ├── db/ # MongoDB implementation
│ │ └── main.rs # Entry point, router setup
│ ├── tests/ # Integration tests
│ ├── Cargo.toml # Rust dependencies
│ └── docker-compose.yml # Docker deployment
│
├── web/ # React frontend
│ └── normogen-web/
│ ├── src/
│ │ ├── pages/ # Page components (Login, Register, etc.)
│ │ ├── components/ # Reusable components
│ │ ├── services/ # API service layer (axios)
│ │ ├── store/ # Zustand state management
│ │ └── types/ # TypeScript type definitions
│ └── package.json
│
├── mobile/ # Mobile app (placeholder)
├── shared/ # Shared code (placeholder)
├── thoughts/ # Development notes
└── docs/ # Organized documentation
├── product/ # Product definition, roadmap
├── implementation/ # Phase plans, specs
├── testing/ # Test scripts & results
├── deployment/ # Deployment guides
└── development/ # Git workflow, CI/CD
🔑 Key Concepts
Backend Architecture
Technology Stack
- Language: Rust (edition 2021)
- Web Framework: Axum 0.7 (async, tower-based)
- Database: MongoDB 7.0
- Authentication: JWT (jsonwebtoken 9)
- Access tokens: 15 minute expiry
- Refresh tokens: 30 day expiry
- PBKDF2 password hashing (100K iterations)
- Security: Rate limiting (tower-governor), audit logging
Project Structure Pattern
The backend follows a modular architecture:
// Entry point: src/main.rs
mod config; // Configuration from environment
mod db; // Database trait + MongoDB impl
mod models; // Data models with repositories
mod auth; // JWT service
mod security; // Security features (audit, sessions, lockout)
mod handlers; // HTTP request handlers
mod middleware; // Middleware (JWT, rate limiting)
mod services; // Business logic (OpenFDA, interactions)
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load config → Connect DB → Initialize services → Build router
}
Handler Pattern
All API handlers follow this pattern:
// In src/handlers/
pub async fn handler_name(
State(state): State<AppState>,
Json(req): Json<RequestType>,
) -> Result<Json<ResponseType>, ApiError> {
// 1. Validate request
// 2. Call service/repository
// 3. Return response
}
Authentication Flow
- User registers/logs in → JWT tokens generated
- Access token in Authorization header →
middleware::jwt_auth_middlewarevalidates - User ID extracted and available in handlers
- Protected routes require valid JWT
Database Pattern
- Repository pattern: Each model has a Repository trait
- MongoDB implementation:
db::MongoDbwrapsmongodb::Client - Collections accessed via
Databasefrom MongoDB
Frontend Architecture
Technology Stack
- Framework: React 19.2.4 + TypeScript 4.9.5
- UI Library: Material-UI (MUI) 7.3.9
- State Management: Zustand 5.0.11
- HTTP Client: Axios 1.13.6
- Routing: React Router DOM 7.13.1
Project Structure
src/
├── pages/ # Route components (LoginPage, RegisterPage, etc.)
├── components/ # Reusable components (ProtectedRoute, etc.)
├── services/ # API service (axios instance with interceptors)
├── store/ # Zustand stores (auth, user, etc.)
├── types/ # TypeScript interfaces (API types)
└── App.tsx # Main app with routing
State Management
- Zustand stores in
src/store/ - Auth store: JWT tokens, user info
- API client: Axios instance with auth header injection
🚀 Common Workflows
Adding a New API Endpoint
Backend (Rust)
# 1. Add model to src/models/mod.rs
# 2. Create repository methods in src/models/your_model.rs
# 3. Add handler to src/handlers/your_handler.rs
# 4. Register route in src/main.rs
# 5. Add tests to tests/
Example:
// src/models/new_feature.rs
use mongodb::{Collection, Database};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct NewFeature {
pub id: String,
pub name: String,
}
pub struct NewFeatureRepository {
collection: Collection<NewFeature>,
}
impl NewFeatureRepository {
pub fn new(db: &Database) -> Self {
Self {
collection: db.collection("new_features"),
}
}
pub async fn create(&self, feature: &NewFeature) -> Result<()> {
self.collection.insert_one(feature).await?;
Ok(())
}
}
// src/handlers/new_feature.rs
use axum::{Json, State};
use crate::models::new_feature::{NewFeature, NewFeatureRepository};
pub async fn create_new_feature(
State(state): State<AppState>,
Json(req): Json<NewFeature>,
) -> Result<Json<NewFeature>, ApiError> {
let repo = NewFeatureRepository::new(&state.db.get_database());
repo.create(&req).await?;
Ok(Json(req))
}
// src/main.rs
.route("/api/new-features", post(handlers::create_new_feature))
Frontend (React/TypeScript)
# 1. Add types to src/types/api.ts
# 2. Add API service to src/services/api.ts
# 3. Create Zustand store in src/store/useStore.ts
# 4. Create page/component in src/pages/ or src/components/
Example:
// src/types/api.ts
export interface NewFeature {
id: string;
name: string;
}
// src/services/api.ts
export const newFeatureApi = {
create: (data: NewFeature) =>
api.post('/api/new-features', data),
getAll: () =>
api.get('/api/new-features'),
};
// src/store/useStore.ts
import { create } from 'zustand';
interface NewFeatureStore {
features: NewFeature[];
fetchFeatures: () => Promise<void>;
}
export const useNewFeatureStore = create<NewFeatureStore>((set) => ({
features: [],
fetchFeatures: async () => {
const response = await newFeatureApi.getAll();
set({ features: response.data });
},
}));
// src/pages/NewFeaturePage.tsx
import { useNewFeatureStore } from '../store/useStore';
export const NewFeaturePage = () => {
const { features, fetchFeatures } = useNewFeatureStore();
useEffect(() => { fetchFeatures(); }, []);
return <div>{/* UI here */}</div>;
};
Running Tests
Backend Tests
# Unit tests
cd backend && cargo test
# Integration tests
cd backend && cargo test --test '*'
# Specific test
cd backend && cargo test test_name
# With output
cd backend && cargo test -- --nocapture
Frontend Tests
cd web/normogen-web && npm test
# Coverage
cd web/normogen-web && npm test -- --coverage
API Endpoint Tests
# Quick smoke test
./docs/testing/quick-test.sh
# Full API test suite
./docs/testing/test-api-endpoints.sh
# Medication-specific tests
./docs/testing/test-medication-api.sh
Deployment
Local Development
cd backend
docker compose up -d
# Check logs
docker compose logs -f backend
# Check health
curl http://localhost:6500/health
Production (Solaria)
# Use deployment script
./docs/deployment/deploy-to-solaria.sh
# Manual deployment
cd backend
docker compose -f docker-compose.prod.yml up -d
🔒 Security Guidelines
Authentication
- All protected routes require JWT in
Authorization: Bearer <token>header - Access tokens expire in 15 minutes
- Refresh tokens expire in 30 days
- Tokens are rotated on refresh
Rate Limiting
- General rate limiting applied to all routes
- Configured in
middleware::general_rate_limit_middleware - Account lockout after 5 failed login attempts (15min base, max 24hr)
Password Security
- PBKDF2 with 100,000 iterations
- Passwords never logged or returned in API responses
- Zero-knowledge recovery phrases
Data Validation
- Request validation using
validatorcrate - Input sanitization in handlers
- MongoDB uses BSON type system
📊 Current Implementation Status
Completed Features ✅
- JWT authentication (login, register, logout, refresh)
- User management (profile, settings, password change)
- Permission-based access control (Read, Write, Admin)
- Share management (share resources with permissions)
- Security hardening (rate limiting, audit logging, session management)
- Medication management (CRUD, dose logging, adherence tracking)
- Health statistics tracking (weight, BP, trends)
- Lab results storage
- OpenFDA integration for drug data
Implemented ✅
- Drug interaction checking (Phase 2.8) —
/api/interactions/check,/check-new
In Progress / Not Started 🚧
- Automated reminder system (Phase 2.8 stretch)
- Frontend integration with backend (Phase 3)
Planned 📋
- Medication refill tracking
- Advanced health analytics
- Healthcare data export (FHIR, HL7)
- Caregiver access
- Mobile app
🛠️ Development Guidelines
Code Style
Rust (Backend)
- Use
cargo fmtfor formatting - Use
cargo clippyfor linting - Follow Rust naming conventions
- Use
Result<_, ApiError>for error handling - Document public APIs with rustdoc comments
TypeScript (Frontend)
- Use functional components with hooks
- Prefer TypeScript interfaces over types
- Use Material-UI components
- Follow React best practices
- Use Zustand for state management
Commit Guidelines
- Use conventional commits:
feat(scope): description - Examples:
feat(backend): implement drug interaction checkingfix(medication): resolve adherence calculation bugdocs(readme): update quick start guidetest(auth): add refresh token rotation tests
Testing Guidelines
- Write tests for new features
- Test edge cases (empty inputs, null values, etc.)
- Use descriptive test names
- Mock external dependencies (OpenFDA API)
- Test both success and error paths
📚 Documentation Navigation
Quick Reference
- Main Documentation Index - Complete documentation overview
- Product Documentation - Project overview, roadmap, status
- Implementation Docs - Phase plans, specs, progress
- Testing Guide - Test scripts and results
- Deployment Guide - Deployment guides and scripts
- Development Workflow - Git workflow, CI/CD
For Specific Tasks
| Task | Documentation |
|---|---|
| Add new API endpoint | Implementation Guide + code examples above |
| Deploy to production | Deployment Guide |
| Run tests | Testing Guide |
| Understand architecture | Product README |
| Check current status | STATUS.md |
| Review phase progress | Implementation README |
🤖 AI Agent Specific Tips
Before Making Changes
- Read the context: Check STATUS.md and Implementation README
- Understand the phase: Know which phase is active (currently 2.8)
- Check existing code: Look at similar implementations in
backend/src/ - Review tests: Check
backend/tests/for test patterns
When Implementing Features
- Follow patterns: Use existing code as templates (see examples above)
- Add tests: Write tests in
backend/tests/or inline with#[cfg(test)] - Update documentation: Add/update docs in
docs/implementation/ - Test thoroughly: Run
cargo test,cargo clippy, and integration tests
When Debugging
- Check logs:
docker compose logs -f backend - Test API: Use scripts in
docs/testing/ - Verify MongoDB:
mongoshto check data - Check recent changes:
git log --oneline -10
Common Pitfalls to Avoid
- Don't hardcode values - Use environment variables (see
backend/src/config/mod.rs) - Don't skip tests - Always run tests before committing
- Don't forget auth - Protected routes need JWT middleware
- Don't ignore errors - Use
?operator and proper error handling - Don't break existing APIs - Check for existing endpoints before adding new ones
File Locations Quick Reference
- Add API handler:
backend/src/handlers/ - Add model:
backend/src/models/ - Add middleware:
backend/src/middleware/ - Add service:
backend/src/services/ - Add tests:
backend/tests/ - Add config:
backend/src/config/mod.rs - Add frontend page:
web/normogen-web/src/pages/ - Add frontend component:
web/normogen-web/src/components/ - Add API service:
web/normogen-web/src/services/api.ts - Add types:
web/normogen-web/src/types/api.ts
📞 Getting Help
Internal Resources
- Thoughts - Development notes and decisions
- Git History - Past changes and context
- Test Results - API test history
External Resources
- Axum Documentation: https://docs.rs/axum/
- MongoDB Rust Driver: https://docs.rs/mongodb/
- React Documentation: https://react.dev/
- Material-UI: https://mui.com/
🔄 Version Information
Current Versions:
- Rust: rustc 1.90.0 (1159e78c4 2025-09-14)
rustc 1.90.0 (1159e78c4 2025-09-14)
- Node: v20.20.0
v20.20.0
- Docker: Docker version 29.2.1, build a5c7197
Docker version 29.2.1, build a5c7197
Repository: origin ssh://git@gitea.soliverez.com.ar/alvaro/normogen.git (fetch)
origin ssh://git@gitea.soliverez.com.ar/alvaro/normogen.git (fetch)
Branch: git branch --show-current
✅ Checklist for AI Agents
Before starting work, ensure you:
- Read STATUS.md for current progress
- Reviewed Implementation README for phase context
- Understand the architecture (backend/frontend structure)
- Know which phase is active (currently 2.8)
- Have reviewed similar existing code
- Understand testing requirements
During work:
- Follow existing code patterns
- Write/update tests
- Run
cargo testandcargo clippy - Document changes in
docs/implementation/ - Update relevant STATUS files
Before completing:
- All tests pass
- Code is formatted (
cargo fmt) - No clippy warnings
- Documentation updated
- Commit message follows conventions
This guide is maintained in: docs/AI_AGENT_GUIDE.md
Last updated: 2026-03-09
Maintained by: Project maintainers
For questions: Consult project documentation or create an issue