# Viamonte Exchange — MLS Architecture v1.0

## Project Overview
**Viamonte Exchange** is a patent-protected platform for direct, intermediary-free exchanges of high-value tangible assets:
- **Real Estate** (California-compliant private sales)
- **Luxury Vehicles** (nationwide)
- **Fine Goods** (nationwide)

The **mls.viamonte.exchange** subdomain serves as the MLS (Multiple Listing Service) / marketplace engine.

---

## Target Stack

| Layer | Technology | Notes |
|-------|-----------|-------|
| **Backend** | Laravel 8.x (PHP 7.4 compatible) | LTS release, cPanel-compatible |
| **Database** | MySQL 8.x / MariaDB 10.x | InnoDB, utf8mb4 |
| **Cache** | Redis (optional, fallback: file cache) | Queue driver, session store |
| **Queue** | Laravel Horizon / Redis or Database | Async document processing, notifications |
| **Frontend** | Vanilla JS SPA (existing) | Client-side routing via hash |
| **API** | RESTful JSON API | Laravel Sanctum token auth |
| **Storage** | Local disk → S3 (phase 2) | Document uploads |
| **Deployment** | cPanel + Git | Composer + npm build pipeline |

---

## Business Viability Assessment

### Strengths
- **Patent-protected** IP — defensible moat
- **Clear niche** — not Zillow (no brokerage), not Poshmark (no retail), pure peer-to-peer high-value swaps
- **California-compliant** — private sale exemption legal framework built in
- **Unit API integration** — banking infrastructure for value difference settlement
- **HelioSync key system** — cryptographic identity binding (differentiator)

### Risks
- **Liquidity problem** — high-value peer matching requires critical mass
- **Regulatory risk** — real estate exchange regulations vary by state
- **Trust barrier** — high-value assets require robust verification

### MVP Viability Score: **8/10**
Core exchange workflow is technically straightforward with Laravel + MySQL.

---

## Entity Relationship Model

```
┌─────────────────┐       ┌──────────────────┐       ┌──────────────────┐
│     users       │       │     assets       │       │   documents      │
├─────────────────┤       ├──────────────────┤       ├──────────────────┤
│ id (UUID)       │──1:N──│ id               │       │ id               │
│ name            │       │ user_id (FK)      │       │ asset_id (FK)    │
│ email           │       │ title            │       │ exchange_id (FK) │
│ password        │       │ category (enum)   │       │ type (enum)      │
│ phone           │       │ description      │       │ filename         │
│ avatar_url      │       │ location         │       │ original_name    │
│ bio             │       │ latitude         │       │ mime_type        │
│ member_since    │       │ longitude        │       │ size             │
│ verified_at     │       │ value (decimal)   │       │ signed_url       │
│ user_key        │       │ status (enum)     │       │ signed_at        │
│ user_key_url    │       │ images (JSON)     │       │ uploaded_by (FK) │
│ created_at      │       │ featured         │       │ created_at       │
│ updated_at      │       │ views_count      │       └──────────────────┘
└─────────────────┘       │ created_at       │
        │                  │ updated_at       │
        │                  └──────────────────┘
        │                         │
        │  ┌──────────────────────┘
        │  │
        │  ▼
        │  ┌──────────────────┐       ┌──────────────────┐
        │  │   exchanges      │       │  exchange_assets │
        │  ├──────────────────┤       ├──────────────────┤
        │  │ id               │──1:N──│ id               │
        │  │ uuid (public)    │       │ exchange_id (FK) │
        │  │ proposer_id (FK) │       │ asset_id (FK)    │
        │  │ counterparty_id  │       │ party (proposer/ │
        │  │ status (enum)    │       │       counterparty)│
        │  │ value_diff (dec) │       │ created_at       │
        │  │ settlement_type  │       └──────────────────┘
        │  │ terms_accepted   │
        │  │ completed_at     │       ┌──────────────────┐
        │  │ created_at       │       │   messages       │
        │  └──────────────────┘       ├──────────────────┤
        │         │                   │ id               │
        │         │                   │ exchange_id (FK) │
        │         ▼                   │ sender_id (FK)   │
        │  ┌──────────────────┐       │ content          │
        │  │   meetings       │       │ read_at          │
        │  ├──────────────────┤       │ created_at       │
        │  │ id               │       └──────────────────┘
        │  │ exchange_id (FK) │
        │  │ proposer_id (FK) │       ┌──────────────────┐
        │  │ counterparty_id  │       │   favorites      │
        │  │ scheduled_at     │       ├──────────────────┤
        │  │ duration_minutes │       │ id               │
        │  │ topic            │       │ user_id (FK)     │
        │  │ video_room_url   │       │ asset_id (FK)    │
        │  │ status (enum)    │       │ created_at       │
        │  │ created_at       │       └──────────────────┘
        │  └──────────────────┘
        │
        ▼
┌──────────────────┐
│  asset_views     │
├──────────────────┤
│ id               │
│ asset_id (FK)    │
│ user_id (FK)     │ (nullable for guests)
│ ip_address       │
│ viewed_at        │
└──────────────────┘
```

---

## Database Migration Definitions

### 1. `users` table
```php
Schema::create('users', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->string('phone')->nullable();
    $table->string('avatar_url')->nullable();
    $table->text('bio')->nullable();
    $table->year('member_since');
    $table->string('user_key', 64)->nullable()->unique();
    $table->string('user_key_url')->nullable();
    $table->boolean('is_verified')->default(false);
    $table->rememberToken();
    $table->timestamps();
    $table->softDeletes();
});
```

### 2. `personal_access_tokens` (Sanctum)
```php
Schema::create('personal_access_tokens', function (Blueprint $table) {
    $table->id();
    $table->morphs('tokenable');
    $table->string('name');
    $table->string('token', 64)->unique();
    $table->text('abilities')->nullable();
    $table->timestamp('last_used_at')->nullable();
    $table->timestamps();
});
```

### 3. `assets` table
```php
Schema::create('assets', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('user_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->enum('category', ['real_estate', 'luxury_vehicle', 'fine_good']);
    $table->text('description');
    $table->string('location');
    $table->decimal('latitude', 10, 7)->nullable();
    $table->decimal('longitude', 10, 7)->nullable();
    $table->decimal('value', 14, 2);
    $table->enum('status', ['available', 'pending', 'exchanged', 'withdrawn'])->default('available');
    $table->json('images')->nullable();
    $table->boolean('featured')->default(false);
    $table->unsignedInteger('views_count')->default(0);
    $table->timestamps();
    $table->softDeletes();
    
    $table->index('category');
    $table->index('status');
    $table->index('value');
    $table->fullText(['title', 'description', 'location']);
});
```

### 4. `exchanges` table
```php
Schema::create('exchanges', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('uuid', 12)->unique(); // public short ID
    $table->foreignUuid('proposer_id')->constrained('users')->cascadeOnDelete();
    $table->foreignUuid('counterparty_id')->constrained('users')->cascadeOnDelete();
    $table->enum('status', ['pending', 'negotiating', 'terms_agreed', 'completed', 'declined', 'withdrawn'])->default('pending');
    $table->decimal('value_difference', 14, 2)->nullable();
    $table->enum('settlement_type', ['cash', 'wire', 'other'])->nullable();
    $table->timestamp('terms_accepted_at')->nullable();
    $table->timestamp('completed_at')->nullable();
    $table->timestamps();
    $table->softDeletes();
    
    $table->index('status');
    $table->index(['proposer_id', 'counterparty_id']);
});
```

### 5. `exchange_assets` pivot table
```php
Schema::create('exchange_assets', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('exchange_id')->constrained()->cascadeOnDelete();
    $table->foreignUuid('asset_id')->constrained()->cascadeOnDelete();
    $table->enum('party', ['proposer', 'counterparty']);
    $table->timestamps();
    
    $table->unique(['exchange_id', 'asset_id']);
});
```

### 6. `documents` table
```php
Schema::create('documents', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('asset_id')->nullable()->constrained()->nullOnDelete();
    $table->foreignUuid('exchange_id')->nullable()->constrained()->nullOnDelete();
    $table->foreignUuid('uploaded_by')->constrained('users');
    $table->enum('type', ['appraisal', 'legal', 'authentication', 'title', 'insurance', 'other']);
    $table->string('filename');
    $table->string('original_name');
    $table->string('mime_type');
    $table->unsignedBigInteger('size');
    $table->string('signed_url')->nullable();
    $table->timestamp('signed_at')->nullable();
    $table->timestamps();
    $table->softDeletes();
});
```

### 7. `messages` table
```php
Schema::create('messages', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('exchange_id')->constrained()->cascadeOnDelete();
    $table->foreignUuid('sender_id')->constrained('users');
    $table->text('content');
    $table->timestamp('read_at')->nullable();
    $table->timestamps();
    
    $table->index('exchange_id');
    $table->index(['sender_id', 'created_at']);
});
```

### 8. `meetings` table
```php
Schema::create('meetings', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('exchange_id')->constrained()->cascadeOnDelete();
    $table->foreignUuid('proposer_id')->constrained('users');
    $table->foreignUuid('counterparty_id')->constrained('users');
    $table->timestamp('scheduled_at');
    $table->unsignedSmallInteger('duration_minutes')->default(30);
    $table->string('topic');
    $table->string('video_room_url')->nullable();
    $table->enum('status', ['scheduled', 'completed', 'cancelled'])->default('scheduled');
    $table->timestamps();
});
```

### 9. `favorites` table
```php
Schema::create('favorites', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('user_id')->constrained()->cascadeOnDelete();
    $table->foreignUuid('asset_id')->constrained()->cascadeOnDelete();
    $table->timestamps();
    
    $table->unique(['user_id', 'asset_id']);
});
```

---

## Laravel Folder Structure

```
/home/viamonte/mls.viamonte.exchange/
├── app/
│   ├── Console/
│   │   ├── Commands/
│   │   │   ├── GenerateUserKeys.php
│   │   │   └── CleanExpiredSessions.php
│   │   └── Kernel.php
│   ├── Enums/
│   │   ├── AssetCategory.php
│   │   ├── AssetStatus.php
│   │   ├── DocumentType.php
│   │   ├── ExchangeStatus.php
│   │   └── MeetingStatus.php
│   ├── Events/
│   │   ├── ExchangeProposed.php
│   │   ├── ExchangeAccepted.php
│   │   ├── MessageSent.php
│   │   └── DocumentUploaded.php
│   ├── Exceptions/
│   │   └── Handler.php
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── Api/
│   │   │   │   ├── AuthController.php
│   │   │   │   ├── AssetController.php
│   │   │   │   ├── ExchangeController.php
│   │   │   │   ├── DocumentController.php
│   │   │   │   ├── MessageController.php
│   │   │   │   ├── MeetingController.php
│   │   │   │   ├── FavoriteController.php
│   │   │   │   ├── ProfileController.php
│   │   │   │   └── SearchController.php
│   │   │   └── Controller.php
│   │   ├── Middleware/
│   │   │   ├── Cors.php
│   │   │   └── ForceJson.php
│   │   └── Requests/
│   │       ├── StoreAssetRequest.php
│   │       ├── UpdateAssetRequest.php
│   │       ├── ProposeExchangeRequest.php
│   │       ├── SendMessageRequest.php
│   │       └── ScheduleMeetingRequest.php
│   ├── Jobs/
│   │   ├── ProcessDocument.php
│   │   ├── SendExchangeNotification.php
│   │   └── GenerateVideoRoom.php
│   ├── Listeners/
│   │   ├── SendExchangeProposalNotification.php
│   │   └── LogAssetView.php
│   ├── Models/
│   │   ├── User.php
│   │   ├── Asset.php
│   │   ├── Exchange.php
│   │   ├── ExchangeAsset.php
│   │   ├── Document.php
│   │   ├── Message.php
│   │   ├── Meeting.php
│   │   └── Favorite.php
│   ├── Providers/
│   │   └── AppServiceProvider.php
│   ├── Services/
│   │   ├── HelioSyncService.php
│   │   ├── ExchangeService.php
│   │   ├── DocumentService.php
│   │   ├── VideoRoomService.php
│   │   └── UnitApiService.php
│   └── Traits/
│       ├── HasUuid.php
│       └── ApiResponse.php
├── bootstrap/
├── config/
│   ├── app.php
│   ├── database.php
│   ├── sanctum.php
│   ├── queue.php
│   └── viamonte.php  # custom config
├── database/
│   ├── migrations/
│   │   ├── 0001_create_users_table.php
│   │   ├── 0002_create_assets_table.php
│   │   ├── 0003_create_exchanges_table.php
│   │   ├── 0004_create_exchange_assets_table.php
│   │   ├── 0005_create_documents_table.php
│   │   ├── 0006_create_messages_table.php
│   │   ├── 0007_create_meetings_table.php
│   │   └── 0008_create_favorites_table.php
│   └── seeders/
│       ├── DatabaseSeeder.php
│       ├── UserSeeder.php
│       └── AssetSeeder.php
├── public/
│   ├── index.php
│   └── .htaccess
├── resources/
│   ├── views/
│   │   └── app.blade.php  # SPA entry point
│   └── lang/
├── routes/
│   ├── api.php
│   ├── web.php
│   └── console.php
├── storage/
│   ├── app/
│   ├── framework/
│   └── logs/
├── tests/
│   ├── Feature/
│   │   ├── AuthTest.php
│   │   ├── AssetTest.php
│   │   └── ExchangeTest.php
│   └── Unit/
└── .env.example
```

---

## API Architecture

### Authentication Flow
```
Client                    Server
  │                         │
  │── POST /api/auth/register ──►  Validate input
  │◄── { user, token }          Create user, generate Sanctum token
  │                             Generate HelioSync user key
  │                             
  │── POST /api/auth/login  ──►  Validate credentials
  │◄── { user, token, key }     Return Sanctum token + user key
  │
  │── GET /api/user         ──►  Authenticate via Bearer token
  │◄── { user, assets, ... }
  │
  │── POST /api/auth/logout ──►  Revoke current token
  │◄── { message }
```

### API Routes

```php
// === Public Routes ===
Route::prefix('api')->group(function () {
    Route::post('auth/register', [AuthController::class, 'register']);
    Route::post('auth/login', [AuthController::class, 'login']);
    
    // Marketplace (public read)
    Route::get('assets', [AssetController::class, 'index']);
    Route::get('assets/{asset}', [AssetController::class, 'show']);
    Route::get('assets/{asset}/similar', [AssetController::class, 'similar']);
    
    // Search
    Route::get('search', [SearchController::class, 'search']);
    Route::get('search/filters', [SearchController::class, 'filters']);

    // === Authenticated Routes ===
    Route::middleware('auth:sanctum')->group(function () {
        // Auth
        Route::post('auth/logout', [AuthController::class, 'logout']);
        Route::get('user', [ProfileController::class, 'show']);
        Route::put('user', [ProfileController::class, 'update']);
        Route::put('user/password', [ProfileController::class, 'updatePassword']);
        
        // Asset CRUD
        Route::post('assets', [AssetController::class, 'store']);
        Route::put('assets/{asset}', [AssetController::class, 'update'])
            ->middleware('can:update,asset');
        Route::delete('assets/{asset}', [AssetController::class, 'destroy'])
            ->middleware('can:delete,asset');
        Route::post('assets/{asset}/images', [AssetController::class, 'uploadImages']);
        Route::get('my-assets', [AssetController::class, 'myAssets']);
        
        // Favorites
        Route::get('favorites', [FavoriteController::class, 'index']);
        Route::post('favorites/{asset}', [FavoriteController::class, 'toggle']);
        
        // Exchanges
        Route::post('exchanges', [ExchangeController::class, 'propose']);
        Route::get('exchanges', [ExchangeController::class, 'index']);
        Route::get('exchanges/{exchange}', [ExchangeController::class, 'show']);
        Route::put('exchanges/{exchange}/accept', [ExchangeController::class, 'accept']);
        Route::put('exchanges/{exchange}/decline', [ExchangeController::class, 'decline']);
        Route::put('exchanges/{exchange}/withdraw', [ExchangeController::class, 'withdraw']);
        Route::put('exchanges/{exchange}/complete', [ExchangeController::class, 'complete']);
        
        // Messages
        Route::get('exchanges/{exchange}/messages', [MessageController::class, 'index']);
        Route::post('exchanges/{exchange}/messages', [MessageController::class, 'store']);
        Route::put('messages/{message}/read', [MessageController::class, 'markRead']);
        
        // Meetings
        Route::post('exchanges/{exchange}/meetings', [MeetingController::class, 'store']);
        Route::get('meetings', [MeetingController::class, 'index']);
        Route::put('meetings/{meeting}', [MeetingController::class, 'update']);
        Route::delete('meetings/{meeting}', [MeetingController::class, 'destroy']);
        
        // Documents
        Route::post('documents/upload', [DocumentController::class, 'upload']);
        Route::get('documents', [DocumentController::class, 'index']);
        Route::get('documents/{document}', [DocumentController::class, 'show']);
        Route::delete('documents/{document}', [DocumentController::class, 'destroy']);
        Route::get('documents/{document}/download', [DocumentController::class, 'download']);
        
        // User Key
        Route::get('user/key', [ProfileController::class, 'keyInfo']);
        Route::post('user/key/regenerate', [ProfileController::class, 'regenerateKey']);
    });
});
```

---

## Queue Architecture

### Queue Configuration
```
QUEUE_CONNECTION=redis   # or 'database' for fallback
```

### Jobs
| Job | Queue | Purpose |
|-----|-------|---------|
| `ProcessDocument` | `documents` | Virus scan, OCR, generate signed URL |
| `SendExchangeNotification` | `notifications` | Email/push when exchange status changes |
| `GenerateVideoRoom` | `meetings` | Create video conference room via Daily.co API |
| `LogAssetView` | `analytics` | Increment view count asynchronously |
| `SyncHelioSyncKey` | `keys` | Register key with userkey.viamonte.exchange |

---

## Agent Architecture

### HelioSync (Cryptographic Identity)
```
User Device                         Viamonte Server
    │                                     │
    │── POST /api/auth/register ──────────►
    │                                     │── Generate Sun Key (ECDH P-256)
    │                                     │── Derive User Key (HKDF-SHA256)
    │                                     │── Store keys in IndexedDB
    │◄── { userKey, sunKeyPublic } ───────│
    │                                     │
    │── Navigate to userkey.viamonte.exchange ──►
    │                                     │── Resolve user key from URL
    │                                     │── Display key card
    │                                     │
    │── Propose Exchange ────────────────►│── Verify counterparty key
```

### Exchange Flow (Workflow)
```
1. Propose  ─►  2. Negotiate  ─►  3. Agree Terms  ─►  4. Sign Docs  ─►  5. Complete
     │              │                    │                   │               │
     │              ▼                    │                   │               │
     │         Messages +                │                   │               │
     │         Video Calls               │                   │               │
     │                                   ▼                   │               │
     │                              Generate                 │               │
     │                              Term Sheet               │               │
     │                                                       ▼               │
     │                                            E-sign via               │
     │                                            Document Service         │
     │                                                                     ▼
     │                                                             Unit API
     │                                                             Settlement
```

---

## Security Requirements

1. **Authentication**: Laravel Sanctum (token-based, SPA-compatible)
2. **Authorization**: Policies for asset/ exchange ownership
3. **API Protection**: Rate limiting (60 req/min for auth, 300 for public)
4. **Input Validation**: Form requests with strict rules
5. **File Uploads**: MIME validation, size limits (25MB), virus scan
6. **CORS**: Tight origin restrictions (viamonte.exchange subdomains)
7. **CSP**: Content-Security-Policy headers
8. **HelioSync**: Keys stored locally in IndexedDB, never on server
9. **Data Encryption**: HTTPS everywhere, encrypted storage for signed docs

---

## Cost Estimate (Monthly)

| Item | Cost | Notes |
|------|------|-------|
| cPanel Hosting (current) | $20-30 | Already provisioned |
| MySQL Database | Included | cPanel includes 1GB |
| Redis (optional) | $15-25 | For queues/cache |
| S3 Storage (phase 2) | $5-10 | Document storage |
| Daily.co (video) | $0-50 | Free tier available |
| Unit API | Usage-based | Banking integration |
| **Total MVP** | **$20-30/mo** | Without Redis |
| **Total Production** | **$70-100/mo** | Full stack |

---

## Implementation Roadmap

### Phase 1 — MVP (Week 1-2) ✅
- [x] HTML/CSS/JS prototype
- [x] HelioSync key system prototype
- [ ] Laravel project setup
- [ ] Database migrations
- [ ] Auth API (register/login)
- [ ] Asset CRUD + marketplace API
- [ ] Basic exchange workflow
- [ ] cPanel deployment

### Phase 2 — Exchange Engine (Week 3-4)
- [ ] Full exchange workflow (propose → negotiate → complete)
- [ ] Message system
- [ ] Meeting scheduling + Daily.co integration
- [ ] Document upload + e-sign
- [ ] Email notifications
- [ ] Unit API bank integration sandbox

### Phase 3 — Production Hardening (Week 5-6)
- [ ] Redis queue workers
- [ ] Rate limiting
- [ ] Admin dashboard
- [ ] KYC verification
- [ ] Analytics dashboard
- [ ] Performance optimization

### Phase 4 — Launch (Week 7-8)
- [ ] Security audit
- [ ] Load testing
- [ ] Legal review
- [ ] User onboarding docs
- [ ] Production launch

---

## Deployment Plan (cPanel)

### Structure
```
/home/viamonte/
├── mls.viamonte.exchange/        # Laravel application
│   ├── public_html/ → symlink
│   │   ├── index.php
│   │   └── .htaccess
│   ├── app/
│   ├── bootstrap/
│   ├── config/
│   ├── database/
│   ├── routes/
│   └── storage/ (symlink to ~/storage)
├── access-logs/
├── etc/
├── ssl/
└── public_html/                  # Main viamonte.exchange
    ├── index.html
    └── ... (existing prototype)
```

### Deployment Steps
1. Initialize Laravel via Composer
2. Configure `.env` with database credentials (from cPanel MySQL)
3. Run migrations
4. Set up cron for scheduler (`php artisan schedule:run`)
5. Set up queue worker (`php artisan queue:work`)
6. Symlink `public/` → `public_html/` for cPanel subdomain
7. Configure SSL via AutoSSL (already present)

---

*Viamonte Exchange — Protected by Patent. Architecture v1.0*
