A modern Enterprise Resource Planning (ERP) system built with Rust and React. Obvia ERP features a multi-tenant architecture designed for scalability and security.
Obvia ERP is designed as a full-stack web application:
- Backend: RESTful API built with Rust using the Axum web framework
- Frontend: React-based single-page application (SPA) with TypeScript
- Architecture: Multi-tenant system with PostgreSQL databases
- Authentication: JWT-based authentication with Argon2 password hashing
- Security: No unsafe code allowed (enforced at compile time)
- Language: Rust
- Web Framework: Axum
- Database: PostgreSQL with SQLx
- Authentication: JWT (jsonwebtoken), Argon2 for password hashing
- Async Runtime: Tokio
- Configuration: TOML-based configuration files (config crate)
- Error Handling: thiserror, anyhow
- Testing: pretty_assertions, mockall
- Framework: React
- Language: TypeScript
- Build Tool: Vite
- Styling: TailwindCSS
- State Management: Redux Toolkit
- Routing: React Router
- UI Components: Radix UI components
- Icons: Lucide React
- Testing: Vitest
- Linting: ESLint with TypeScript ESLint
obvia/
├── backend/ # Backend Rust application
│ ├── src/
│ │ ├── main.rs # Main application entry point
│ │ ├── common/ # Common utilities and types
│ │ ├── manager/ # System-wide operations (auth, users, tenants)
│ │ └── tenant/ # Tenant-specific business logic
│ ├── config/ # Configuration files
│ │ └── default.toml.example # Example configuration
│ ├── migrations/ # Database migrations
│ │ ├── main/ # Main database migrations
│ │ └── tenant/ # Tenant database migrations
│ ├── Cargo.toml # Rust package manifest
│ └── Dockerfile # Backend container definition
├── frontend/ # Frontend React application
│ ├── src/ # Frontend source code
│ ├── public/ # Static assets
│ ├── package.json # Node.js dependencies and scripts
│ ├── vite.config.ts # Vite build configuration
│ ├── tsconfig.json # TypeScript configuration
│ ├── eslint.config.js # ESLint configuration
│ ├── httpd.conf # Apache HTTP server configuration
│ └── Dockerfile # Frontend container definition
├── CONTRIBUTING.md # Contribution guidelines
├── CONTRIBUTORS.md # List of contributors
├── DCO # Developer Certificate of Origin
├── LICENSE # GNU AGPL v3 license
├── Jenkinsfile # CI/CD pipeline configuration
└── README.md # This file
- Rust: 1.90.0 or later
- PostgreSQL: Database server (version 12 or later recommended)
- Cargo: Package manager (included with Rust)
- Node.js: 24 or later
- npm: Package manager (included with Node.js)
- Docker: For containerized deployment
- Docker Compose: For local development with containers
-
Install Rust (if not already installed):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
-
Navigate to the backend directory:
cd backend -
Configure the application:
cp config/default.toml.example config/default.toml
Edit
config/default.tomlwith your database credentials and settings. See the Configuration section for details. -
Build the backend:
# Development build cargo build # Production build (optimized) cargo build --release
-
Run the backend:
# Development mode cargo run --bin obvia_backend # Production mode (after release build) ./target/release/obvia_backend
The backend will start on
http://0.0.0.0:3000(or the configured host/port). Database migrations will be automatically executed on startup.
-
Navigate to the frontend directory:
cd frontend -
Install dependencies:
npm install
-
Run the development server:
npm run dev
The frontend will be available at
http://localhost:5173(default Vite port).Note: The development server is configured to proxy API requests from
/apitohttp://localhost:3000(the backend server). Ensure the backend is running before making API calls. -
Build for production:
npm run build
The production build will be output to the
dist/directory.
The backend uses TOML configuration files located in backend/config/. Create default.toml from the example file and
customize it for your environment.
Key Configuration Sections:
[server]
bind_address = "0.0.0.0" # Server host address
bind_port = 3000 # Server port
public_base_url = "example.com" # Server hostname
environment = "prod" # Server environment
log_level = "trace"[main_database]
host = "localhost" # PostgreSQL host
port = 5432 # PostgreSQL port
username = "db_user" # Database username
password = "db_pass" # Database password
database = "obvia_main" # Main database name
pool_size = 10 # Connection pool size[auth]
jwt_secret = "your_super_secret_jwt_key" # Secret key for JWT signing (change this!)
jwt_issuer = "obvia" # JWT issuer
jwt_audience = "obvia_users" # JWT audience
jwt_expiration_mins = "480" # Token expiration (8 hours)
access_token_expiration_mins = 5 # Access token expiration in mins
refresh_token_expiration_mins = 480 # Refresh token expiration in minsmail_enabled = true # Is mailing enabled
smtp_host = "smtp_host" # SMTP host
smtp_user = "smtp_user" # SMTP user
smtp_passwd = "smtp_passwd" # SMTP password
default_from = "default_from" # Default from address
default_from_name = "default_from_name" # Default from display name
default_notification_email = "default_notification_email" # Default admin notification emailImportant: Always change the jwt_secret to a strong, random value in production environments.
The frontend can use environment variables during the build process:
VITE_GIT_COMMIT_HASH: Git commit hash (automatically set during Docker build)
Additional environment variables can be added following
the Vite environment variable conventions (prefix with VITE_).
The frontend development server (npm run dev) is configured to proxy API requests:
- All requests to
/api/*are forwarded tohttp://localhost:3000 - This is configured in
frontend/vite.config.ts
The backend uses inline test modules (co-located with source code) and includes unit tests with mocking support.
Run all backend tests:
cd backend
cargo testRun tests with output (shows println! statements):
cargo test -- --nocaptureRun specific test module or function:
cargo test <module_or_function_name>Run tests with verbose output:
cargo test --verboseRun tests in watch mode (default for Vitest):
cd frontend
npx vitestRun tests once and exit:
cd frontend
npx vitest runRun these commands from the backend/ directory:
cargo build: Build the backend in debug modecargo build --release: Build optimized production binarycargo run --bin obvia_backend: Run the backend in development modecargo test: Run all testscargo clippy: Run linter for code quality checkscargo fmt: Format Rust source filescargo check: Check for compilation errors without building
Run these commands from the frontend/ directory:
npm run dev: Start development server with hot reload (port 5173)npm run build: Build for production (TypeScript compilation + Vite build)npm run preview: Preview production build locallynpm run lint: Run ESLint for code qualitynpx vitest: Run tests with Vitest
Backend:
cd backend
docker build -t obvia-backend .Frontend:
cd frontend
docker build --build-arg VITE_GIT_COMMIT_HASH=$(git rev-parse --short HEAD) -t obvia-frontend .Backend (exposes port 3000):
docker run -v /path/to/config:/opt/config -p 3000:3000 obvia-backend Frontend (exposes port 80):
docker run -p 80:80 obvia-frontendThe frontend container uses Apache HTTP Server (httpd 2.4) to serve the static build.
Both Dockerfiles support multi-platform builds:
- Backend: Supports
linux/amd64andlinux/arm64with cross-compilation
The application automatically runs database migrations on startup using SQLx. Migration files are located in:
backend/migrations/main/: Main database schema (system-wide data)backend/migrations/tenant/: Tenant-specific schema
Migrations are executed in order based on their timestamp prefix. The migration system tracks which migrations have been applied to avoid re-running them.
Obvia ERP implements a multi-tenant architecture with separate database schemas:
-
Manager Module (
backend/src/manager/): Handles system-wide operations including:- Authentication and authorization
- User management
- Tenant management
-
Tenant Module (
backend/src/tenant/): Contains tenant-specific business logic:- Products
- Services
- Customers
- Taxes
- And other tenant-isolated data
- No Unsafe Code: The package is configured to deny unsafe code via Clippy metadata and
#![forbid(unsafe_code)] - Password Hashing: Uses Argon2 for secure password hashing
- JWT Authentication: Stateless authentication with configurable expiration
- SQL Injection Protection: All database queries use parameterized queries via SQLx
We welcome contributions! Please read CONTRIBUTING.md for details on:
- How to submit feature requests and bug reports
- Code contribution guidelines
- Developer Certificate of Origin (DCO) requirements
- Code review process
- Testing requirements
Important: All commits must be signed off using git commit -s to comply with the DCO.
Contributors are listed in CONTRIBUTORS.md.
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
See the LICENSE file for the full license text.
- ✅ You can use, modify, and distribute this software
- ✅ You can use it for commercial purposes
⚠️ If you modify and deploy this software (including as a web service), you must make your source code available under the same license⚠️ Network use counts as distribution under AGPL (unlike GPL)⚠️ You must include the original copyright notice and license text
For questions or support, please contact:
- Email: kapcsolat@kovacsdavid.dev
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.