# Project Mycelium - Master Architecture Guide v2.0
## Complete Single Source of Truth for AI-Assisted Development
**Document Purpose**: Comprehensive architectural reference consolidating ALL design decisions, implementation patterns, roadmaps, and development standards for the Project Mycelium. This serves as the definitive guide for AI coders, developers, and system architects.
**Last Updated**: 2025-08-15 12:41
**Architecture Version**: 3.2
**Status**: Production Implementation Active
---
## π― Executive Summary
The Project Mycelium has evolved into a production-ready, industry-standard platform built on comprehensive builder pattern architecture, log-free codebase principles, and persistent data-only operations. The system eliminates all mock data, centralizes all construction patterns, and maintains zero compilation errors through systematic architectural decisions.
### Key Achievements (2025)
- β
**881+ Log Statements Removed**: Complete log-free codebase
- β
**245+ ResponseBuilder Patterns Migrated**: 10 controllers complete, 1 in progress
- β
**1,120+ Lines of Code Reduced**: Through builder pattern consolidation
- β
**100% Mock Data Elimination**: Persistent data-only architecture
- β
**Zero Compilation Errors**: Clean builds maintained throughout
- β
**Single Source of Truth**: All construction patterns centralized
- β
**TFC Credits System**: Industry-standard credit model (1 TFC = 1 USD)
- β
**Major Controllers Completed**: Marketplace, Rental, Pool, Order, Simple Controllers
- β
**Critical Milestone**: Dashboard compilation errors fixed (18 errors β 0 errors)
- β
**Public Pages Added**: Changelog and Roadmap pages with comprehensive project status
- β
**Public Controller**: New PublicController for legal, changelog, and roadmap pages
- β
**Authentication & Buy-Now Flow Fixed**: Critical wallet balance issue resolved
- β
**Middleware Authentication**: Fixed route exclusion and session validation
- β
**Frontend-Backend Integration**: ResponseBuilder pattern compatibility established
- β
**Order Management System**: Complete user-centric order persistence and display
- β
**Payment Method Persistence**: Enhanced UX with last payment method remembering
- β
**Authentication Flow Enhancement**: Improved buy-now messaging and redirect to dashboard
- β
**Preferred Currency Displayed Throughout**: Navbar, Wallet, Orders, and Dashboard use server-formatted values; dashboard overview wallet label made dynamic via navbar dropdown API
---
## ποΈ Core Architectural Principles
### 1. **Log-Free Codebase Policy** π«π
**Policy**: Zero `log::` statements in main/development branches
**Implementation**:
- Developers may use `log::` for troubleshooting in feature branches
- All logs must be removed before merging to main/development
- Automated cleanup: `find src -name "*.rs" -exec perl -i -pe 'BEGIN{undef $/;} s/\s*log::[^;]*;//g' {} \;`
**Benefits**:
- Clean, production-ready code
- Simplified AI-assisted development
- Reduced file complexity and size
- Eliminated performance overhead
### 2. **Builder Pattern Architecture** ποΈ
**Philosophy**: Single-source-of-truth construction for all complex objects
**Core Builders**:
- `SessionDataBuilder`: UserPersistentData construction
- `ConfigurationBuilder`: Environment variable access
- `ResponseBuilder`: HTTP response handling
- `ServiceFactory`: Service instantiation
#### **ResponseBuilder Pattern - Critical Frontend Integration** β οΈ
**IMPORTANT**: All API responses are automatically wrapped by ResponseBuilder
**Response Structure**:
```json
// Backend Controller Code:
ResponseBuilder::ok().json(serde_json::json!({
"success": true,
"last_payment_method": "credit_card"
})).build()
// Actual API Response (wrapped by ResponseBuilder):
{
"data": {
"success": true,
"last_payment_method": "credit_card"
},
"success": true
}
```
**Frontend Compatibility Pattern**:
```javascript
// β
CORRECT - Handle ResponseBuilder wrapping
const response = await fetch('/api/endpoint');
const result = await response.json();
const data = result.data || result; // Handle both wrapped and unwrapped
if (data.success && data.field) {
// Use data.field
}
// β INCORRECT - Direct access fails with ResponseBuilder
if (result.success && result.field) {
// This fails because actual data is in result.data.field
}
```
**Implementation Examples**:
- **Orders**: `data.data.orders` (nested due to ResponseBuilder + order response structure)
- **Payment Methods**: `data.last_payment_method` (single ResponseBuilder wrapping)
- **Wallet Balance (Navbar dropdown)**: `data.wallet_balance_formatted` and `data.display_currency` from `/api/navbar/dropdown-data`
- **Wallet Balance (direct API, if used)**: `data.balance` or similar numeric field; always unwrap via `const data = result.data || result;`
**Key Rules for AI Coders**:
1. **Always use**: `const data = result.data || result;` for API responses
2. **Test both**: Wrapped and unwrapped response structures
3. **Debug with**: `console.log('API response:', result);` to see actual structure
4. **Remember**: ResponseBuilder adds automatic `{"data": {...}, "success": true}` wrapper
### CSP-Compliant Frontend: External JS + JSON Hydration (2025-08-11)
**Policy**: Zero inline scripts and zero inline event handlers. All page behavior lives in external JS with JSON hydration blocks.
**Implementation**:
- Externalized base and page scripts to `src/static/js/` (e.g., `base.js`, `wallet.js`, `statistics.js`).
- Added `{% block scripts %}{% endblock %}` in `src/views/base.html` for page-specific includes.
- Pages provide a `
```
**Recent Fix (root cause of server exit)**:
- Tera failed to parse `src/views/wallet/index.html` due to invalid filter usage inside JSON and quoting issues.
- Fixed by using the named-arg form `default(value='$')` and emitting JSON via `json_encode()`.
- Added explicit Tera init logging in `src/main.rs` so startup errors print to stderr:
- File: `src/main.rs` β log `Tera initialization error: {e}` before exiting.
- Outcome: `cargo run --bin projectmycelium` now keeps the Actix server running; template errors are clearly reported.
**Benefits**:
- Security: Strict CSP compliance (no inline code) reduces XSS risk.
- Maintainability: Clear separation of structure (HTML), data (hydration JSON), and behavior (external JS).
- Performance: External JS is cacheable across pages; smaller HTML payloads.
- Testability: Hydration data is deterministic JSON; easier unit/integration tests.
- Consistency: One binding pattern via `DOMContentLoaded` and data attributes, not scattered inline handlers.
**Rules of use**:
- Never use `onclick`/`onchange` in templates; bind events in JS on `DOMContentLoaded`.
- Always emit hydration via `` and stable container id `id="services-grid"`.
- Implemented `src/static/js/services.js` to bind `.add-to-cart-btn` clicks, show auth modal on 401, listen for `serviceCreated` events, and render service cards without inline handlers.
- Integrates with `base.js` globals when present (`updateCartCount`, `emitCartUpdated`).
#### Dashboard Orders Page Externalization β Completed 2025-08-11
- Removed all inline scripts and event handlers from `src/views/dashboard/orders.html`.
- Added CSP-safe hydration block `#orders-hydration` with per-field JSON encoding:
```html
```
- Included external script: `src/static/js/dashboard_orders.js`.
- Event delegation via `data-action` attributes:
- `toggle-details`, `view-invoice`, `contact-support`, `view-invoice-from-modal`.
- ResponseBuilder compatibility handled in JS: `const payload = result.data || result;`.
- Tera parse fix: replaced quoted defaults inside JSON with named-arg defaults and `json_encode()` per field to emit valid JSON; resolved the startup error βexpected a value that can be negated or an array of valuesβ.
#### β
CSP Externalization COMPLETE β 2025-08-12
**ACHIEVEMENT**: The entire Project Mycelium is now 100% CSP-compliant with zero inline handlers across all templates.
**Dashboard Service Provider Externalization β COMPLETED**:
- Removed inline handlers for availability and service creation in `src/views/dashboard/service_provider.html` by using `data-action` attributes and external JS.
- Implemented delegated handlers in `src/static/js/dashboard-service-provider.js`:
- `availability.toggle` β `toggleAvailability()` for checkbox changes (no API write).
- `availability.update` β `updateAvailability()` PUT `/api/dashboard/availability` with validation and notifications.
- `services.create` β `createNewService()` opens modal, validates, submits to API, and refreshes lists.
- Fixed and initialized `loadAvailabilitySettings()` on `DOMContentLoaded` to hydrate UI safely under CSP.
- Added delegated handler: `data-action="services.saveChanges"` β calls existing `saveServiceChanges()` for the Save Changes button (no inline JS).
- Hydration: confirmed CSP-safe JSON block `#sp-dashboard-hydration` present and parsed in initialization.
- Tera parse fix applied to `src/views/dashboard/service_provider.html`: moved page-specific styles to `{% block head %}` and hydration/external scripts to `{% block scripts %}` at the top level; `dashboard_content` now contains only markup/modals.
**Final CSP Cleanup β COMPLETED 2025-08-12**:
- β
`src/views/cart.html` β Converted `editCartItem()` & `removeCartItem()` to `data-action="cart.edit"` & `data-action="cart.remove"`
- β
`src/views/dashboard/user.html` β Converted `viewBookingDetails()` & `contactProvider()` to `data-action="booking.view"` & `data-action="provider.contact"`
- β
`src/views/dashboard/farmer.html` β Converted 6 inline handlers to data-action patterns:
- `refreshSliceCalculations()` β `data-action="slice.refresh"`
- `syncWithGrid()` β `data-action="grid.sync"`
- `viewNodeSlices()` β `data-action="node.view"`
- `setMaintenanceMode()` β `data-action="node.maintenance"`
- `restartNode()` β `data-action="node.restart"`
#### Create Service Modal Flicker β Root Cause Fix (2025-08-12)
- __Root cause__: Background elements (cards, rows, service items) had broad `transition: all` and hover effects. During `.modal.fade`, these hover transitions triggered expensive repaints, causing visible flicker on modal buttons.
- __Fix__: Preserve Bootstrap fade while eliminating repaint conflicts.
- Tighten transitions to specific properties (e.g., `box-shadow`, `transform`, `background-color`).
- Suppress hover-driven transitions while any modal is open via `body.modal-open` rules (reset transforms/box-shadows/bg-color to static values).
- Restore `.modal.fade` on `#createServiceModal` to keep smooth animation.
- Add light GPU compositing hints on modal buttons (`will-change`, `transform: translateZ(0)`, `backface-visibility: hidden`).
- Verified single-initialization for delegated handlers and Enter-key submit prevention on modal forms.
- __Files changed__: `src/static/css/styles.css`, `src/views/dashboard/service_provider.html`.
**CSP Verification Results β 2025-08-12**:
- β
**Zero inline handlers found** across all templates (confirmed via comprehensive audit)
- β
**All external JS files exist** and properly structured
- β
**JSON hydration blocks** in place for CSP-safe data passing
- β
**Print utilities** confirmed available
**β
COMPLETE: Global CSP Compliance Achieved (100%)**:
- Dashboard templates
- β
`src/views/dashboard/wallet.html` (done)
- β
`src/views/dashboard/pools.html` (done)
- β
`src/views/dashboard/service_provider.html` (COMPLETED)
- β
`src/views/dashboard/user.html` (COMPLETED 2025-08-12)
- β
`src/views/dashboard/farmer.html` (COMPLETED 2025-08-12)
- β
`src/views/dashboard/orders.html` (done)
- β
`src/views/dashboard/cart.html` (done)
- β
`src/views/dashboard/welcome.html` (no inline handlers found)
- Dashboard JS
- β
`src/static/js/dashboard_pools.js`
- β
`src/static/js/dashboard-service-provider.js` (COMPLETED)
- β
`src/static/js/dashboard_user.js` (verified existing)
- β
`src/static/js/dashboard_farmer.js` (verified existing)
- β
`src/static/js/dashboard_orders.js` (done)
- β
`src/static/js/dashboard_cart.js` (done)
- β
`src/static/js/print-utils.js` (verified existing)
- Marketplace templates
- β
`src/views/marketplace/services.html` (done)
- β
`src/views/marketplace/checkout.html` (done)
- β
`src/views/cart.html` (COMPLETED 2025-08-12)
- β
All other marketplace templates (no inline handlers found)
- Marketplace JS
- β
`src/static/js/services.js` (done)
- β
`src/static/js/checkout.js` (done)
- β
`src/static/js/cart.js` (verified existing)
- β
All other required JS files (verified existing)
**Security & Compliance Benefits Achieved**:
- β
**Strict CSP compliance** β No inline code execution vectors
- β
**XSS risk reduction** β All user interactions via external event delegation
- β
**Maintainability** β Clean separation of HTML structure, JSON data, and JS behavior
- β
**Performance** β External JS cacheable across pages, smaller HTML payloads
- β
**Testability** β Deterministic hydration JSON, easier unit/integration tests
#### Tera JSON Hydration & Template Safety Guidelines (Must Read)
To avoid Tera parse errors and ensure CSP-safe, valid JSON in hydration blocks, follow these rules:
- __Always encode each field individually__
- Use: `{{ value | default(value=...) | json_encode() }}` per field.
- Strings: use named-arg defaults, e.g., `default(value='USD')`.
- Optionals: use `default(value=null)` to produce `null`.
- Note: Avoid `default(value=none)` as some template contexts can treat `none` as an unresolved variable. If in doubt, prefer explicit `null` or guard with `{% if var is defined %}{{ var | json_encode() }}{% else %}null{% endif %}`.
- __Do NOT create an object literal inside a single interpolation__
- Donβt: `{{ { "user_currency": user_currency, "cart_details": cart_details } | json_encode }}`
- This can trigger: βexpected a value that can be negated or an array of valuesβ.
- __Recommended pattern for hydration blocks__
- Build the JSON shape literally and encode each field:
```html
```
- __When to use `safe`__
- `type="application/json"` blocks are not executed; JSON encoding avoids HTML escaping.
- `safe` is typically unnecessary if you encode each field. If you emit one fully-encoded JSON string, you may append `| safe`.
- __General Tera tips__
- Prefer named arguments in filters: `default(value='...')`.
- Never mix template control structures inside JSON literals.
- Log startup errors: we print Tera init errors in `src/main.rs` to surface issues early.
- Validate JSON quickly by copying the block into a linter when in doubt.
__Checkout fix (2025-08-11)__: `src/views/marketplace/checkout.html` switched from a single interpolated object to the per-field encoding pattern above, resolving the runtime Tera parse error.
#### Tera Block Structure β Common Pitfall (Fixed 2025-08-12)
Tera blocks cannot be nested inside other blocks. Child templates must keep structural blocks (`head`, `scripts`) at the top level and place page markup inside the content block only.
- __Do__:
- Put page styles inside `{% block head %}...{% endblock %}`.
- Put hydration JSON and external JS includes inside `{% block scripts %}...{% endblock %}`.
- Keep `dashboard_content` (or `content`) limited to HTML and modal markup only.
- __Don't__:
- Nest `{% block scripts %}` or style blocks inside `dashboard_content`.
- __Recent fix__: `src/views/dashboard/service_provider.html` raised an "unexpected `{% endblock %}`" parse error due to nested blocks. We refactored by moving:
- Styles β top-level `{% block head %}`
- Hydration JSON (`#sp-dashboard-hydration`) and external JS includes β top-level `{% block scripts %}`
- Result: Tera parses cleanly; CSP structure is preserved.
### DevX: Rust Error-Only Compilation Logs β Added 2025-08-12
To streamline debugging and keep output readable, we added a small toolchain to capture only Rust compiler errors (no warnings) from `cargo check`:
- **Script**: `scripts/dev/cargo-errors.sh`
- **Make targets**:
- `make check-errors`
- `make fixtures-errors` (runs with fixtures env)
- **Output**: `/tmp/cargo_errors_only.log` (override with `OUT=/path/to/file.log`)
- **Dependencies**: Prefers `jq` for precise filtering; falls back to a sed/awk parser if `jq` is not installed.
Usage examples:
```bash
# Standard
make check-errors
# Fixtures mode
make fixtures-errors
# Custom output path
OUT=/tmp/my_errors.log make check-errors
# Quick count/peek
grep -c '^error' /tmp/cargo_errors_only.log
sed -n '1,80p' /tmp/cargo_errors_only.log
```
Behavior:
- Suppresses warnings entirely; logs only error diagnostics.
- Returns `cargo check` exit code (nonβzero when errors present), so it fits CI or hooks.
- Color is disabled via `CARGO_TERM_COLOR=never` to keep the log clean.
### 3. **Persistent Data Only** πΎ
**Policy**: No mock data in production code
- All data operations use `user_data/` directory
- Real user data drives marketplace content
- MockDataService completely eliminated
#### **User-Centric Data Storage Pattern** π―
**Architectural Decision**: Orders stored as part of user's persistent data (`UserPersistence`)
- **Localized Source of Truth**: Each user owns their complete data set
- **Industry Standard**: Follows GDPR compliance and data portability requirements
- **Implementation**: Orders stored in `UserPersistentData.orders` vector
- **Data Flow**: Purchase β UserPersistence β API β ResponseBuilder β Frontend
- **Benefits**: Simplified data management, easier account operations, better user privacy
**Order Management Implementation**:
- `instant_purchase.rs`: Orders pushed to `persistent_data.orders` vector
- `order.rs`: `get_user_orders()` reads from UserPersistence, sorts by `created_at` DESC
- `orders.html`: Frontend handles nested ResponseBuilder JSON structure (`data.data.orders`)
- **ResponseBuilder Compatibility**: All API responses wrapped in `{"data": {...}, "success": true}`
#### Marketplace Order Invoices (2025-08-09)
- Endpoint: `GET /orders/{id}/invoice` renders an HTML invoice view for printing.
- Controller: `OrderController::get_order_invoice` in `src/controllers/order.rs`.
- Ownership check prefers `session["user_email"]` for user identity.
- Dual-source order retrieval: first check in-memory `OrderStorage`; if not found, fallback to persistent `UserPersistence` (email-based). This fixes buy-now invoice access.
- Template: `src/views/marketplace/order_invoice.html` renders order id, date, items, qty, unit price, line totals, subtotal/total, payment method, billing email, and includes a Print button.
- Frontend UI: `src/views/dashboard/orders.html` opens the HTML view in a new tab.
- CTA text changed to "View Invoice" with eye icon.
- JS handlers renamed: `downloadInvoice` β `viewInvoice`, `downloadInvoiceFromModal` β `viewInvoiceFromModal`.
- Not implemented (by design): direct download API (`/api/orders/{id}/invoice`).
- Rationale: Current UX uses "View Invoice" + browser Print β Save as PDF; avoids extra dependencies/complexity; no immediate need for programmatic downloads. Can be added later if integrations require it.
#### Service Requests and Bookings Flow β Current Status and Fix Plan (2025-08-14)
β’ Current state
- Providers can create services; buyers can purchase them.
- Buyer sees entries under βMy Service Requestsβ (`/dashboard/user`).
- Provider sees entries under βService Requestsβ (`/dashboard/service-provider`).
- Provider lookup/persistence fixed: requests saved under correct provider file.
β’ Issues observed
- Status mismatch: when provider accepts (In Progress), buyer still sees Pending.
- View CTA error: βCannot read properties of undefined (reading 'id')β (likely envelope/shape mismatch).
- Update CTA causes the request to disappear (tab filtering not refreshed or destructive overwrite/mock fallback).
- Buyer booking details view shows only a success button (insufficient detail rendering).
β’ Backend actions
- Synchronize status updates across users:
- In `DashboardController::update_service_request_progress` (and `update_service_request`), update both:
1) Provider `service_requests` in `user_data/{provider}.json`.
2) Buyer `service_bookings` in `user_data/{buyer}.json`.
- Use stable identifiers: `request.id`, `customer_email`, and optionally `linked_booking_id`.
- Ensure non-destructive in-place updates via `UserPersistence` (find by id; mutate fields; keep others).
- Add structured logs: before/after counts, updated ids, file paths; bubble errors.
- Normalize details endpoints to a consistent DTO and ResponseBuilder envelope:
- `GET /api/dashboard/service-requests/{id}/details`, `.../completed-details`, `.../report` β return `{ success, data: { id, status, product, customer_email, ... } }`.
β’ Frontend actions
- Always unwrap API with `const data = result.data || result;` and reference `data.id`.
- After updates, re-render tables and migrate rows between tabs (Pending β In Progress β Completed) instead of removing.
- Improve buyer βMy Service Bookingsβ details to render provider/product/status timeline.
- Guard null/undefined records and missing properties.
β’ Testing
- E2E: provider creates β buyer purchases β provider accepts.
- Assert provider request status and buyer booking status both advance to In Progress.
- API contract tests: details endpoints include `id` and consistent field names.
β’ Observability
- Add structured logging to update endpoints and persistence helpers to trace ids, counts, and file paths during updates.
#### Progress Update β Service Requests & Bookings (2025-08-14 13:35)
- Removed inline mock `client_requests` arrays in `src/models/user.rs` (replaced with `Vec::new()`), aligning with the persistentβdataβonly policy and fixing compile errors from new fields.
- Confirmed `ServiceRequest` includes `hours_worked: Option` and `notes: Option` with `#[serde(default)]`; all construction sites initialize them (e.g., `src/services/order.rs` sets `None`).
- Controllers `dashboard::update_service_request` and `dashboard::update_service_request_progress` now normalize responses via `ResponseBuilder`, reload the updated request, and synchronize the buyerβs `service_bookings` with the providerβs `service_requests`.
- Persistence (`UserPersistence`): added/used helpers to update buyer booking fields; persist `hours_worked` and `notes`; set `completed_date` when status is Completed or progress β₯ 100.
- Build status: `cargo check` passes; only warnings remain (unrelated to this flow).
Next:
- Remove remaining inline mocks (e.g., `revenue_history`) and source exclusively from persistent storage.
- Audit update paths to ensure `APP_ENABLE_MOCKS=0` has zero mock fallbacks.
#### Progress Update β Service Provider Dashboard Persistent Data (2025-08-15 08:46)
- Template refactor: `src/views/dashboard/service_provider.html` now binds exclusively to injected persistent context `service_provider_data` (e.g., `active_services`, `total_clients`, `monthly_revenue_usd`). Removed all `user.mock_data.service_provider_data.*` references.
- Hydration: The `#sp-dashboard-hydration` JSON block outputs the persistent `service_provider_data` using `json_encode()` for CSP-safe parsing.
- Frontend JS: `src/static/js/dashboard-service-provider.js` aligned to persistent USD fields (`monthly_revenue_usd`, `total_revenue_usd`, `price_per_hour_usd`). Fallback init payload keys updated to `*_usd`. ResponseBuilder unwrap pattern enforced (`const data = result.data || result;`). Removed legacy/mock field usage.
- Backend controller: `DashboardController::service_provider_section` constructs and injects persistent `ServiceProviderData` via `UserPersistence` (summary metrics, services, client requests). No mock fallbacks remain. Placeholder series like `revenue_history` are empty-by-design, not mock-driven.
- Verification: `cargo check` passes; manual dashboard UI test under `APP_ENABLE_MOCKS=0` confirms cards, tables, and charts render from persistent data/hydration.
Next:
- Apply the same persistent-only mapping to `/dashboard/app-provider` and `/dashboard/user` (templates + JS + controller sections). Ensure `*_usd` naming in UI logic and remove any `user.mock_data` access.
- Replace/remove any remaining placeholder arrays (e.g., `revenue_history`) with persisted metrics or clearly mark as not-yet-implemented (no mock values).
#### Progress Update β App Provider Dashboard Persistent Data (2025-08-15 10:30)
- Frontend JS (`src/static/js/dashboard-app-provider.js`):
- Removed legacy `monthly_revenue` fallbacks and any `sessionStorage` merges; all revenue logic uses persistent `*_usd` fields only (e.g., `monthly_revenue_usd`).
- Treated deployment status "Running" as "Active" for UI consistency.
- Exposed `window.__appProviderDashboard` for modal helpers to estimate revenue.
- Updated deployment details modal to use estimated deployment revenue derived from app-level revenue.
- Backend (`src/controllers/dashboard.rs`):
- Fixed `GET /api/dashboard/deployment/{id}` to return proper JSON success/failure envelopes via `ResponseBuilder`.
- `json_to_app` helper reads `monthly_revenue_usd` with a soft fallback to legacy `monthly_revenue` for backward compatibility.
- Verification: UI tested under `APP_ENABLE_MOCKS=0`; charts, tables, and modals source only persistent data. ResponseBuilder unwrap pattern enforced on the frontend.
#### Progress Update β Dashboard Overview Activities Mapping β COMPLETED (2025-08-15 10:30)
- Root cause: `src/views/dashboard/index.html` referenced `activity.date`, but backend provided `UserActivity` without a `date` field, causing a Tera render error.
- Fix: In `src/controllers/dashboard.rs`, map `UserActivity` into a template-friendly `recent_activities` table with fields:
- `date`: formatted from `timestamp` as `%Y-%m-%d %H:%M`
- `action`: derived from `ActivityType` (e.g., Login, Purchase, Deployment, ...)
- `status`: from `metadata.status` when present; defaults to `Active` for deployments, otherwise `Completed`
- `details`: from `description`
- Outcome: `/dashboard` renders successfully with persistent activities. No template changes required; context now matches template expectations.
#### Progress Update β Mock Gating & Status Normalization (2025-08-15 12:41)
- Backend: Gated legacy mock initialization in OAuth flow. `src/controllers/gitea_auth.rs` now attaches `MockUserData::new_user()` only when `APP_ENABLE_MOCKS` is enabled via `get_app_config().enable_mock_data()`.
- Model/Docs: Normalized deployment status nomenclature to βActiveβ. Updated `src/services/slice_calculator.rs` comment from "Running" β "Active" for deployment status examples.
- Build: `cargo check` passing after changes (warnings only).
Recent related changes:
- Mock data normalization: In `src/models/user.rs` mock `DeploymentStat` for "Enterprise AI Suite", status was changed from "Running" β "Active".
- Frontend demo: `src/static/js/demo-workflow.js` emits "Active" in simulated `deploymentStatusChange` events.
Next:
- Audit all controllers for unconditional mock usage (e.g., `src/controllers/dashboard.rs`) and gate behind `APP_ENABLE_MOCKS`.
- Normalize remaining "Running" β "Active" across:
- Rust builders: `src/models/builders.rs` (`DeploymentStatBuilder` default status)
- Frontend: `src/static/js/marketplace-integration.js`, `src/static/js/dashboard-user.js`
- Templates: any status badge strings
- Run `cargo test` and perform manual UI verification with `APP_ENABLE_MOCKS=0`.
#### User Data Loading & Scan Hardening β COMPLETED 2025-08-14 15:29
β’ Problem
- Multiple modules scanned `user_data/` broadly and attempted to parse non-user fixture files (e.g., `session_data.json`, carts, aggregated fixtures), causing serde parse errors and runtime failures.
β’ Design Decision
- Enforce a strict, uniform filter for per-user JSON files across the entire codebase.
- Criteria for a valid user file:
- Filename ends with `.json`
- Filename contains `_at_` (email encoding)
- Filename does NOT contain `_cart`
- Filename is not `session_data.json`
β’ Implementation (Rust)
- Updated directory scans to apply the filter in:
- `src/services/node_marketplace.rs` β `get_all_marketplace_nodes()`, `get_all_slice_combinations()`
- `src/services/node_rental.rs` β `find_node_owner()`
- `src/services/order.rs` β `find_app_provider()`, `find_service_provider()`
- `src/controllers/dashboard.rs` β deployment counting logic
- `src/utils/data_cleanup.rs` β `DataCleanup::cleanup_all_users()`
- `src/utils/data_validator.rs` β `DataValidator::validate_all_user_files()`
- Confirmed existing aggregators in `src/services/user_persistence.rs` already applied equivalent filtering for services/apps/products.
β’ Behavior & Guarantees
- Only real per-user files are parsed; non-user fixtures are ignored.
- Eliminates deserialization errors and runtime crashes from non-user files.
- Provider dashboard data accesses go through `UserPersistence`; no production mock fallbacks (`APP_ENABLE_MOCKS=0`).
β’ Build & Verification
- `cargo check` passed after changes.
- Integration tests that parse user JSON remain valid; further E2E will confirm dashboard stability.
#### Insufficient Balance β Unified Error Contract (2025-08-09)
- Decision: Standardize the API error response for insufficient funds across checkout, buy-now, and cart-related validations.
- Status Code: Recommend 402 Payment Required as the single status for insufficient funds. If current implementations differ, document and migrate to 402.
- Canonical Response (via ResponseBuilder):
```json
{
"success": false,
"error": {
"code": "INSUFFICIENT_FUNDS",
"message": "Insufficient balance",
"details": {
"currency": "USD",
"wallet_balance_usd": 0,
"required_usd": 0,
"deficit_usd": 0
}
}
}
```
- Apply in:
- Controllers/Services that perform funds checks: `src/controllers/order.rs`, `src/services/order.rs`, `src/services/instant_purchase.rs`
- Wallet-dependent controllers: `src/controllers/wallet.rs`, `src/controllers/pool.rs`, `src/controllers/rental.rs`
- Frontend Guidance:
- Consume `error.details` to render a consistent message: "Insufficient balance. Need $ more." (currency-aware)
- Do not hardcode numbers; read from `details`. Maintain `const data = result.data || result;` wrapper tolerance.
- Testing:
- API tests: assert status code and JSON shape for checkout and buy-now flows (and cart validations if applicable).
- UI tests/steps: verify consistent rendering and clear CTAs (e.g., "Add Funds").
- Compatibility:
- If legacy shapes exist, document temporary adapter logic and a removal timeline.
### Product Data Architecture: User-Owned Products & Derived Catalog (2025-08-10)
**Decision**: Products are owned by the creating user (single source of truth). The marketplace catalog is a derived read model. Avoid dual writes.
- Single Source of Truth (now): user-persistent storage via `UserPersistence` (per-user JSON) holds canonical product records owned by that user (`provider_id`/`user_email`).
- Catalog (read model): `ProductService` aggregates user-owned products (and fixture seeds in fixtures mode), dedupes by `id`, and derives categories at runtime. Do not write to the catalog directly.
- Fixtures Mode: `user_data/products.json` is seed/demo-only and not a write target. Optionally, a dev-only generated cache `user_data/catalog.products.json` may be produced by an indexing task and must be marked as generated.
- Implemented: id-based deduplication in `ProductService::get_all_products()` prevents overlap between seeds and user-owned items. Later sources override earlier ones to honor the SOT.
- Consistency: `ProductService::get_product_by_id()` now queries the aggregated, de-duplicated catalog to ensure the same precedence rules apply to detail views.
#### Category ID Normalization (Fixtures) β 2025-08-10
- Rationale: Historical fixture data used plural/alias category IDs that did not match frontend filters/routes.
- Implementation: During fixture load, `ProductService` normalizes category IDs to canonical singular forms (e.g., "applications" β "application", "gateways" β "gateway").
- Data Hygiene: `user_data/products.json` is updated to store singular `category_id` values.
- Outcome: All marketplace category views (`/marketplace/applications`, `/marketplace/gateways`, etc.) display the correct products.
#### Provider β Marketplace β Consumer Flow β Implemented 2025-08-12
- Creation (Provider)
- Service/App creation via dashboard APIs:
- Services: `GET/POST /api/dashboard/services`, `PUT /api/dashboard/services/{id}`, `DELETE /api/dashboard/services/{id}`
- Apps: `GET/POST /api/dashboard/apps`, `PUT /api/dashboard/apps/{id}`, `DELETE /api/dashboard/apps/{id}`
- Generic products: `GET/POST /api/dashboard/products`
- Persistence: `UserPersistence` writes to `user_data/{email}.json` under `products` (single source of truth).
- Aggregation (Marketplace)
- `src/services/product.rs::ProductService::get_all_products()` aggregates fixtures + user products (+ optional slice products) and de-duplicates by `id`.
- Category normalization applied uniformly via `canonical_category_id` (see below).
- Optional dev TTL cache controlled by `APP_CATALOG_CACHE` and `APP_CATALOG_CACHE_TTL_SECS`.
- Visibility (Consumer)
- `src/controllers/marketplace.rs::MarketplaceController::services()` filters by canonical categories `service` and `application`, converts prices to user currency, and injects `service_products` into `src/views/marketplace/services.html`.
- Template contract: Each entry is `{ product, price, formatted_price }`. Page is CSP-compliant with JSON hydration and external JS (`src/static/js/services.js`).
- Purchase Flows
- Add to Cart: `POST /api/cart/add` β `OrderController::add_to_cart`.
- Buy Now:
- Affordability: `GET /api/wallet/check-affordability?amount=`
- Instant Purchase: `POST /api/wallet/instant-purchase`
- ResponseBuilder envelope: Frontend unwraps with `const data = result.data || result;`.
- Generalization
- App providers follow the same flow; canonical `application` category is normalized and displayed on `/marketplace/applications` with equivalent pricing and purchase behaviors.
#### Category Normalization (User Products) β 2025-08-12
- Problem: Providers historically saved subcategory IDs (e.g., `Consulting`, `Deployment`) causing products to be filtered out of `/marketplace/services`.
- Solution: `src/services/product.rs::canonical_category_id` maps professional service subcategories (Consulting, Deployment, Support, Training, Development, Maintenance) to canonical `service`.
- Result: User-created services stored in `user_data/{email}.json` are visible post-aggregation; controller still tolerates both `service` and `application` during transition.
#### Catalog Dev Cache (Development) β 2025-08-10
- Purpose: Speed up local development by caching the aggregated catalog derived from fixtures, user-owned products, and optional slice combinations.
- Implementation: In-memory TTL cache inside `ProductService` keyed by `include_slice_products`.
- Structure: `static CATALOG_CACHE: OnceLock>` holding two buckets (with/without slices).
- Entry: `CacheEntry { products: Vec, fetched_at: Instant }`.
- Behavior: `get_all_products()` checks TTL and returns cached vector; on miss/expiry, recomputes via `aggregate_all_products_uncached()` and stores new entry. De-duplication semantics are preserved.
- Configuration:
- Flags: `APP_CATALOG_CACHE` (true/false), `APP_CATALOG_CACHE_TTL_SECS` (u64 seconds).
- Defaults: Development/Test enabled with TTL=5s; Production disabled unless explicitly enabled.
- Accessors: `AppConfiguration::is_catalog_cache_enabled()` and `catalog_cache_ttl_secs()`.
- Scope & Consistency:
- Caches only the aggregated catalog vector; downstream helpers (e.g., categories) continue to derive from that consistent list.
- Slice toggle maintains distinct caches; category normalization and id-based dedupe remain intact.
- Invalidation & Non-goals (Phase 0):
- No write-through invalidation yet; rely on small TTL for freshness during dev.
- Manual bust options: restart the server or wait for TTL to lapse. Changing env TTL requires restart to take effect.
- Code pointers: `src/services/product.rs::ProductService::get_all_products()`, `aggregate_all_products_uncached()`; configuration in `src/config/builder.rs`.
- Generated artifacts: If you generate `user_data/catalog.products.json`, keep it out of VCS (e.g., add to `.gitignore`).
- Acceptance:
- Manual test: results update after TTL; no duplicate products; category pages reflect normalized IDs.
- Build: `cargo check` passes.
- Phase Roadmap:
- Phase 0 (now): In-memory TTL cache for dev.
- Phase 1: Optional dev-only cache-bust endpoint or keyboard shortcut in debug UI.
- Phase 2: Optional fine-grained caches (by category/provider) or Redis toggle for production scalability.
#### Marketplace Overview Composition & De-duplication β 2025-08-10
- Overview Sections: The dashboard composes multiple product sections, notably "Featured Items" (curated) and "Popular Applications" (category-filtered).
- De-duplication Rule: Items already rendered in "Featured Items" are excluded from "Popular Applications" to prevent duplicate cards.
- Implementation: `src/controllers/marketplace.rs` collects featured product IDs into a set and filters the popular list prior to price conversion and render.
- Scope: De-duplication is local to the overview page; category/detail pages show full results without cross-section filtering.
#### Migration Blueprint: PostgreSQL + PostgREST
- Tables:
- `users(id uuid pk, email text unique, ...)`
- `products(id uuid pk, owner_user_id uuid fk, name text, description text, category_id text, base_price numeric(18,8), base_currency text, availability text, attributes jsonb, metadata jsonb, status text check in (draft,published,archived), created_at timestamptz, updated_at timestamptz)`
- `categories(id text pk, name text, ... )`
- Views/Indexes:
- `public_products` view exposing only `status = 'published'` rows for catalog queries
- GIN/trigram indexes on searchable text/jsonb fields for efficient search
- RLS Policies:
- Owners: `INSERT/UPDATE/DELETE` where `owner_user_id = auth.uid()`
- Public: `SELECT` from `public_products` only
- Auth via JWT (GoTrue) used by PostgREST
- Lifecycle:
- Publishing controlled by `status` (draftβpublishedβarchived), separate from `availability` (e.g., InStock/OutOfStock)
- Prefer soft-delete with audit columns over hard delete
Env/Mode Notes:
- Fixtures: `APP_DATA_SOURCE=fixtures`, `APP_FIXTURES_PATH=./user_data`, `APP_ENABLE_MOCKS=0`
- Mock (dev-only): `APP_DATA_SOURCE=mock APP_ENABLE_MOCKS=1` to visualize legacy mock data during development.
- Personas for demos: `user1..user10@example.com` with password `password` (see `docs/dev/design/current/ux/current_personas.md`)
- Manual test checklist: `docs/dev/design/current/ux/manual_fixture_test_checklist.md`
Quick .env for Phase 0 (fixtures/dev):
```dotenv
APP_DATA_SOURCE=fixtures
APP_FIXTURES_PATH=./user_data
APP_ENABLE_MOCKS=0
APP_CATALOG_CACHE=true
APP_CATALOG_CACHE_TTL_SECS=5
```
### Roadmap to Final Marketplace (2025-08-10)
Phases are incremental and shippable; each has crisp acceptance criteria.
#### Phase 0: Polish fixtures/dev mode (nowβshort)
- Catalog dev cache: Optional generated `user_data/catalog.products.json` from aggregator; documented as generated-only.
- Acceptance: cold-start marketplace loads β₯300 ms faster locally; cache invalidation documented.
- Mock cleanup: Ensure prod path never touches mock data/services; annotate remaining dev-only templates (e.g., mock timeline) and gate behind config.
- Acceptance: production run with `APP_ENABLE_MOCKS=0` has zero mock reads/writes.
- Tests:
- Invoices: ownership checks, 403/404, print view loads.
- Cart badge/events: add/remove/clear flows consistent after reload.
- Marketplace dashboard loads with mocks disabled (HTTP 200).
- Rental endpoints return 404 when mocks are disabled and resource is missing.
- Acceptance: tests green in CI; fixtures-run smoke passes.
#### Phase 1: Unified insufficient balance contract
- Backend: Standardize on 402 Payment Required and canonical ResponseBuilder error payload across `src/controllers/order.rs`, `src/services/order.rs`, `src/services/instant_purchase.rs`, and wallet-related controllers.
- Reference: See prompt doc `docs/dev/design/current/prompts/prompt-1.md` β section "π₯ Critical: Insufficient Balance β Unified Error Contract" for the JSON envelope and acceptance details.
- Frontend: One renderer consumes `error.details` (e.g., βInsufficient balance. Need $X more.β).
- Acceptance: Each major purchase flow has one insufficient-funds behavior; e2e verified.
#### Phase 2: Orders API enrichment β COMPLETED 2025-08-13
- Added `invoice_available` and `invoice_url` in `/api/orders` and `/api/orders/{id}`.
- UI enables/disables invoice CTAs from payload.
- Acceptance: No dead CTAs; invoices open consistently from both list and detail.
#### Phase 3: Provider and catalog readiness
- Minimal βpublishing statusβ on products (draft/published) respected by aggregator (fixtures treat all as published).
- Featured curation source-of-truth file (config JSON) for Featured Items, decoupled from categories.
- Acceptance: Only published items appear; Featured fully driven by config.
#### Phase 4: Search, filters, and category UX
- Add keyword search and common filters (category, price range, availability).
- Acceptance: Search/filters consistent across overview and category pages; no duplicates with Featured on overview.
#### Phase 5: Database migration (PostgreSQL + PostgREST)
- Implement schema and `public_products` view with RLS as per blueprint in this guide.
- Wire `ProductService` reads to PostgREST in βdb modeβ; keep fixtures mode intact for demos.
- Migration scripts + seed path for demo data.
- Acceptance: App runs in db mode with same UX; fixtures mode remains supported for demos.
#### Phase 6: Payments and wallet top-up
- Stripe integration to purchase TFC credits; lock rates at checkout; record payment method.
- Commission logic applied to marketplace orders.
- Acceptance: Successful top-up flows; orders complete with correct balances and auditable records.
#### Phase 7: Security and reliability
- CSRF, rate limiting, session hardening; structured logging where appropriate; metrics & health endpoints.
- Acceptance: Security checklist green; basic SLOs monitored.
#### Phase 8: Launch readiness
- CI/CD, load testing, PWA/performance polish, accessibility, documentation/runbooks.
- Acceptance: Go-live checklist complete; rollback plan defined.
### 4. **TFC Credits System** π°
**Decision**: Pure ThreeFold Credits (1 TFC = 1 USD configurable)
- Eliminates complex TFP/USD conversion logic
- Industry-standard credit model (AWS/Google/Azure pattern)
- Simplified user experience and developer maintenance
---
### 5. Cart Count Consistency (Navbar Badge)
Backend (`src/services/order.rs`):
- `OrderService::get_cart_with_details()` cleans orphaned cart items (missing products), recomputes `item_count` from valid items, and persists cleaned carts to session and user storage.
- `remove_from_cart()`, `update_cart_item_quantity()`, and `clear_cart()` save to session and, for logged-in users, to persistent JSON under `user_data/*_cart.json`.
- `transfer_guest_cart_to_user()` merges guest items into the user cart on login and saves.
Frontend:
- Global `updateCartCount()` in `src/views/base.html` fetches `/api/cart` with `cache: 'no-store'` and updates the navbar badge.
- `window.updateCartCount` is called on `DOMContentLoaded` and on a global `cartUpdated` event.
- Pages that modify the cart call `window.updateCartCount()` and dispatch `cartUpdated` after changes.
- `src/views/marketplace/dashboard.html` uses a locally named `updateCartCountLocal()` to avoid overriding the global function.
Result: After restarts or rebuilds, the navbar badge accurately reflects the backend cart state.
### 6. Cart Clear UX (Post-Reload Success Toast)
Rationale: After clearing the cart, we force a short-delayed full page reload to guarantee a consistent empty-state UI. To preserve positive UX, a success toast is shown after the reload.
Pattern:
- Before reload (after successful DELETE /api/cart):
- Call `window.emitCartUpdated(0)` and `window.updateCartCount()` to update the navbar badge immediately.
- Set a session flag: `sessionStorage.setItem('cartCleared', '1')`.
- `setTimeout(() => window.location.reload(), 50);`
- On page load:
- In `DOMContentLoaded`, check the flag; if present, remove it and show a success toast.
Implementation:
- Guest Cart: `src/views/cart.html`
- Sets `cartCleared` flag before reload.
- On load, shows `showToast('Cart cleared', 'success')` and removes the flag.
- Dashboard Cart (logged-in): `src/views/dashboard/cart.html`
- Sets `cartCleared` flag before reload; sets `window._suppressCartLoadToast = true` to avoid false error toasts during reload.
- On load, shows `showToast('Cart cleared', 'success')` and removes the flag.
- Marketplace Cart View: `src/views/marketplace/cart.html`
- Sets `cartCleared` flag before reload.
- On load, shows local `showSuccess('Cart cleared')` and removes the flag.
Notes:
- All flows include credentials where required and handle ResponseBuilder responses safely (204/non-JSON tolerant where applicable).
- This pattern ensures both guest and logged-in users receive immediate feedback post-reload without UI flicker or mixed toasts.
### 7. Cart Fetch & Event Standardization (2025-08-09)
To ensure consistent cart state, session handling, and UI updates across all views, cart-related reads and mutations have been standardized.
- Global Event Helper:
- Use `window.emitCartUpdated(cartCount?)` instead of manually dispatching `CustomEvent('cartUpdated', ...)`.
- Global badge updater lives in `src/views/base.html` as `window.updateCartCount()` and listens to `cartUpdated`.
- Reads (`GET /api/cart`):
- Always include `{ cache: 'no-store', credentials: 'same-origin' }` to avoid stale data and ensure cookies/sessions.
- Mutations (POST/PUT/DELETE to `/api/cart` and `/api/cart/item/{id}`):
- Always include `{ credentials: 'same-origin' }`.
- Robustness:
- Tolerant JSON parsing where 204/No Content or non-JSON responses can occur.
- Affected Templates:
- `src/views/dashboard/cart.html`
- `src/views/marketplace/cart.html`
- `src/views/marketplace/cart_full.html`
- `src/views/marketplace/cart_standalone.html`
- `src/views/cart.html` (guest)
- Outcome:
- Navbar badge updates instantly and reliably across guest and logged-in flows.
- No stale reads; session credentials are sent for all cart operations.
## ποΈ System Architecture
### Application Layer Structure
```
src/
βββ controllers/ # HTTP request handlers
β βββ auth.rs β
ResponseBuilder complete (10 patterns)
β βββ wallet.rs β
ResponseBuilder complete (49 patterns)
β βββ product.rs β
ResponseBuilder complete (7 patterns)
β βββ currency.rs β
ResponseBuilder complete (12 patterns)
β βββ marketplace.rs β
ResponseBuilder complete (44 patterns)
β βββ rental.rs β
ResponseBuilder complete (24 patterns)
β βββ pool.rs β
ResponseBuilder complete (13 patterns)
β βββ order.rs β
ResponseBuilder complete (26 patterns)
β βββ debug.rs β
ResponseBuilder complete (1 pattern)
β βββ gitea_auth.rs β
ResponseBuilder complete (2 patterns)
β βββ public.rs β
Uses render_template utility (no direct patterns)
β βββ dashboard.rs β
ResponseBuilder complete (331 patterns)
βββ services/ # Business logic layer
β βββ farmer.rs β
ServiceFactory migrated
β βββ currency.rs β
ServiceFactory migrated
β βββ user_persistence.rs β
ServiceFactory migrated
β βββ session_manager.rs β
ServiceFactory migrated
β βββ node_rental.rs β
ServiceFactory migrated
β βββ slice_rental.rs β
ServiceFactory migrated
β βββ order.rs β
ServiceFactory migrated
βββ models/ # Data structures and builders
β βββ builders.rs β
SessionDataBuilder complete
βββ utils/ # Utility functions and builders
β βββ response_builder.rs β
Centralized HTTP responses
β βββ configuration.rs β
ConfigurationBuilder complete
β βββ mod.rs β
render_template utility integrated with ResponseBuilder (HTML support)
β βββ data_cleanup.rs β
Log-free utilities
βββ user_data/ # Persistent data storage
βββ *.json # User persistent data files
βββ *_cart.json # User cart persistence
```
---
## π Implementation Status
### Completed Migrations β
#### 1. **Log-Free Codebase** (100% Complete)
- **Scope**: Entire codebase (24 files)
- **Impact**: 881+ log statements removed
- **Result**: Clean, production-ready code
#### 2. **SessionDataBuilder** (100% Complete)
- **Scope**: All UserPersistentData construction
- **Impact**: ~800 lines of code reduced
- **Usage**: `SessionDataBuilder::load_or_create(email)`
#### 3. **ConfigurationBuilder** (100% Complete)
- **Scope**: All environment variable access
- **Impact**: ~150 lines of code reduced
- **Usage**: `ConfigurationBuilder::new().jwt_secret().build()`
#### 4. **ServiceFactory** (100% Complete)
- **Scope**: All service instantiation
- **Impact**: ~100+ lines of code reduced
- **Usage**: `ServiceFactory::currency_service()`
#### 5. **ResponseBuilder** (521 patterns complete)
- **Auth Controller**: 10/10 patterns β
- **Wallet Controller**: 49/49 patterns β
- **Product Controller**: 7/7 patterns β
- **Currency Controller**: 12/12 patterns β
- **Marketplace Controller**: 44/44 patterns β
- **Rental Controller**: 24/24 patterns β
- **Pool Controller**: 13/13 patterns β
- **Order Controller**: 26/26 patterns β
- **Debug Controller**: 1/1 patterns β
- **Gitea Auth Controller**: 2/2 patterns β
- **Public Controller**: β
Uses render_template utility (no direct patterns)
- **Dashboard Controller**: 331/331 patterns β
(COMPLETE - 100% migrated)
- **Utils Module**: 1/1 patterns β
(render_template function complete with HTML support)
### Completed β
#### 1. **Dashboard Controller ResponseBuilder Migration Complete**
- **Status**: β
**COMPLETE** - All 331/331 patterns successfully migrated
- **Achievement**: 100% ResponseBuilder pattern coverage across entire codebase
- **Patterns Migrated**: 17 HttpResponse patterns including redirects, JSON responses, and error handling
- **Quality**: Zero compilation errors maintained throughout entire migration
- **Testing**: Full functional verification completed - dashboard application runs successfully
---
## π οΈ Technical Specifications
### ResponseBuilder API
```rust
// Success responses
ResponseBuilder::ok().json(data).build()
// Error responses
ResponseBuilder::bad_request().json(error_data).build()
ResponseBuilder::unauthorized().json(auth_error).build()
ResponseBuilder::internal_error().json(server_error).build()
// Specialized responses
ResponseBuilder::not_found().json(not_found_error).build()
ResponseBuilder::payment_required().json(payment_error).build()
// Plain text responses
ResponseBuilder::internal_error().body("Template rendering failed").build()
```
### SessionDataBuilder Pattern
```rust
// Create new user
let user_data = SessionDataBuilder::new_user(email);
// Load existing or create new
let user_data = SessionDataBuilder::load_or_create(email);
// Automatic field inclusion via Default
UserPersistentData {
user_email: email.to_string(),
wallet_balance_usd: dec!(0),
..Default::default() // Includes all new fields automatically
}
```
---
## π Strategic Roadmap
### Database Evolution: Supabase Architecture
#### Current State
- **Storage**: JSON file-based (`user_data/` directory)
- **Scalability**: Limited to single-node deployment
- **Features**: Basic CRUD operations
#### Target Architecture: Self-Hosted Supabase
- **Database**: PostgreSQL with ACID compliance
- **API**: PostgREST auto-generated REST endpoints
- **Auth**: GoTrue JWT-based authentication
- **Real-time**: WebSocket subscriptions
- **Management**: Supabase Studio web interface
#### Three-Phase Deployment Strategy
**Phase 1: Development/Testing**
- Local Ubuntu 24.04 with Docker Compose
- Complete TFPβTFC refactor
- Migrate from JSON to PostgreSQL
- Access: http://localhost:8000
**Phase 2: Production (Single Node)**
- ThreeFold Grid Ubuntu 24.04 VM
- Same Docker Compose as development
- Production deployment with acceptable SPOF
- Cost-effective, decentralized
**Phase 3: High Availability**
- k3s cluster with multiple masters + workers
- Tools: tfgrid-k3s or k3scluster
- Zero downtime, automatic failover
- Horizontal scaling capabilities
### Complete Marketplace Ecosystem
#### Payment Integration Strategy
```mermaid
graph TD
A[User Browses Marketplace] --> B[Selects App/VM/Service]
B --> C[Add to Cart]
C --> D[Review Cart]
D --> E{Payment Method}
E -->|Has TFC Credits| F[Pay with TFC]
E -->|Needs Credits| G[Buy TFC with Stripe]
G --> H[Stripe Payment]
H --> I[TFC Credits Added]
I --> F
F --> J[Service Deployment Queue]
J --> K[TFC β TFT Conversion]
K --> L[ThreeFold Grid Deployment]
L --> M[Service Active]
```
#### Revenue Model & Commission Structure
- **Marketplace Commission**: 5-15% on all transactions
- **Payment Processing**: Stripe fees (2.9% + $0.30)
- **Grid Deployment**: TFT conversion at market rates
- **Provider Revenue**: 85-95% after commissions
#### Deployment Automation Pipeline
```rust
// Core deployment service structure
pub struct DeploymentJob {
pub id: String,
pub user_id: String,
pub service_spec: ServiceSpec,
pub tfc_amount: Decimal,
pub status: DeploymentStatus,
}
impl DeploymentQueue {
pub async fn process_deployment(&self, job: DeploymentJob) -> Result<(), DeploymentError> {
// 1. Calculate commission
let commission = job.tfc_amount * self.commission_rate;
let deployment_amount = job.tfc_amount - commission;
// 2. Convert TFC to TFT
let tft_amount = self.convert_tfc_to_tft(deployment_amount).await?;
// 3. Deploy to ThreeFold Grid using GridDriver
let deployment = self.grid_driver.deploy_service(&job.service_spec, tft_amount).await?;
// 4. Update user with deployment details
self.update_user_deployment(job.user_id, deployment).await?;
Ok(())
}
}
```
#### ThreeFold Grid Integration
**GridDriver**: Official ThreeFold Grid deployment interface
- **Repository**: https://github.com/threefoldtech/griddriver
- **Purpose**: Standardized API for ThreeFold Grid deployments
- **Features**: VM deployment, Kubernetes clusters, storage allocation
- **Integration**: Direct Rust bindings for marketplace automation
- **Benefits**: Official support, maintained by ThreeFold team, production-ready
---
## π Development Standards
### Code Quality Requirements
- **Zero Compilation Errors**: All migrations maintain clean builds
- **Minimal Warnings**: Only unused variable warnings acceptable
- **Builder Pattern Usage**: Mandatory for complex object construction
- **Persistent Data Only**: No mock data in production code
- **Log-Free Code**: No `log::` statements in main/development branches
### Migration Methodology
- **Manual Systematic Migration**: Preferred over automated scripts
- **Bulk All-at-Once Edits**: Efficient for simple controllers
- **Section-by-Section**: For complex controllers (dashboard, order)
- **Compilation Verification**: `cargo check` after each batch
- **Exact Target Content**: Precise pattern matching to avoid errors
---
## π§ Decision Trail & Historical Context
### Decision #001: TFP β TFC Credits System Refactor
**Date**: 2025-08-04
**Status**: β
**APPROVED** - Implementation Complete
#### Problem Statement
The marketplace used a hybrid TFP (ThreeFold Points) to USD conversion system that created:
- Complex conversion logic throughout the codebase
- Confusing user experience with dual currency displays
- Maintenance overhead with exchange rate calculations
#### Solution: TFC (ThreeFold Credits)
- **Model**: 1 TFC = 1 USD (configurable)
- **Benefits**: Industry standard (AWS/Google/Azure pattern)
- **Implementation**: Global rename TFP β TFC, remove conversion logic
- **User Experience**: Simple mental model, familiar credit system
### Decision #002: Log-Free Codebase Policy
**Date**: 2025-08-05
**Status**: β
**IMPLEMENTED** - 881+ logs removed
#### Rationale
- Production-ready code should not contain debug logging
- Simplified AI-assisted development and code analysis
- Reduced file complexity and compilation overhead
- Industry best practice for clean codebases
### Decision #003: Builder Pattern Architecture
**Date**: 2025-08-03
**Status**: β
**IMPLEMENTED** - Single source of truth
#### Benefits Achieved
- 95.8% compilation error reduction (24 β 0 errors)
- ~1,120+ lines of code reduction
- Future-proof field additions via `..Default::default()`
- Consistent patterns across entire codebase
---
### Decision #004: Multi-Currency Display with USD Settlement
**Date**: 2025-08-07
**Status**: β
**IMPLEMENTED**
#### Summary
- **Design**: Users can select a display currency (USD, TFC, CAD, EUR, β¦). All settlements occur in USD (via Stripe at a later stage).
- **Backend Support**: Added currencies in `src/services/currency.rs::CurrencyService::get_supported_currencies()`
- TFC: `Custom("credits")`, `exchange_rate_to_base = 1.0`, `decimal_places = 2`, `is_active = true`
- CAD: `Fiat`, `exchange_rate_to_base = 1.35` (placeholder), `decimal_places = 2`, `is_active = true`
- EUR: already supported
- **Controller**: Preference and listing endpoints already validate/accept these currencies.
- File: `src/controllers/currency.rs`
- **UI Fix**: Removed duplicate USD and added TFC to the currency selector.
- File: `src/views/dashboard/settings.html`
#### Rationale
- Standard e-commerce pattern: multi-currency display with single-currency settlement simplifies accounting and Stripe integration.
- Keeps USD as canonical base for pricing, accounting, and refunds.
#### Implementation Notes
- Store canonical `base_usd_amount` on orders, plus `display_currency`, `display_amount`, `rate_used`, and `timestamp` for transparency.
- Server-side conversions via `CurrencyService.convert_amount()`; do not trust client-side for totals.
- Navbar dropdown API (`/api/navbar/dropdown-data`) provides fully formatted wallet balance (`wallet_balance_formatted`) and `display_currency` for consistent UI display.
- Dashboard overview wallet section (`src/views/dashboard/index.html`) updates `#dashboardWalletBalance` and `#dashboardCurrencyCode` on load using:
- Safe unwrap pattern: `const data = result.data || result;`
- Values: `data.wallet_balance_formatted` and `data.display_currency`
#### UX Guidance
- Show both: e.g., `Total: $123.45 USD (β 167.89 CAD)` and note: βfinal charge in USD; conversions approximate.β
- On rate refresh/expiry, prompt user to accept updated totals before checkout.
#### Future Enhancements (Optional)
- Populate currency selector dynamically from `/api/currency/supported` to avoid UI/backend drift.
- Add rate locking at checkout and audit trails for exchange rates used per order.
---
### Decision #005: Externalized Dashboard Scripts & JSON Hydration
**Date**: 2025-08-11
**Status**: β
IMPLEMENTED
#### Summary
- Removed inline JavaScript containing template syntax from `src/views/dashboard/index.html` to eliminate template/JS lint conflicts and improve maintainability.
- Introduced a safe data hydration pattern via `