# Corrosion — Platform Architecture & Specification

**Product:** Corrosion — Rust Server Management Platform
**Domain:** corrosionmgmt.com
**Author:** Vantz Stockwell — Vigil Cyber LLC
**Version:** 1.0
**Date:** February 14, 2026

---

## Executive Summary

Corrosion is a hosted SaaS platform that gives Rust server administrators a complete management interface — from wipe automation to plugin management to community storefronts — without requiring any technical infrastructure on their end. Customers install a single uMod plugin, register online, and manage everything from the web.

The platform eliminates the complexity that keeps casual server owners from running professional communities. One plugin. One dashboard. Everything works.

---

## Product Vision

**One sentence:** A Rust server admin installs one plugin, registers online, and never has to SSH into their box, edit a config file, or babysit a wipe again.

**The problem:** Running a Rust server well requires juggling multiple tools, editing JSON configs over SFTP, manually timing wipes, hand-installing plugins, and being online when things break. The barrier to entry is high, and even experienced admins waste hours on operational tasks instead of building their community.

**The solution:** Corrosion moves all server administration to a hosted web platform. The game server runs a lightweight plugin that communicates with Corrosion's cloud via NATS. Everything else — configuration, plugin management, wipe automation, player moderation, community websites, item stores — happens in the browser.

---

## Business Model

### Licensing

| Tier | Price | What They Get |
|------|-------|---------------|
| Base License | $50 one-time (CodeFling) | Core platform access: server management, auto-wiper, plugin installer, public site, analytics, RBAC, all Phase 1-3 features |
| Module Store | $9.99–$24.99 per module | Add-on modules (loot manager, event plugins, etc.) tied to their license key |
| Webstore Add-on | $10/month | Integrated item store with PayPal processing |
| Cloud Backup | $X/month | Off-server backup storage on Corrosion infrastructure |
| Site License (B2B) | $XXX/month | Hosting company integration — bulk licensing for all their Rust customers |

### License Rules

- One license = one server = one subdomain
- Multi-admin with unlimited team members per license (RBAC)
- License key validated on plugin startup and periodic check-in
- Module purchases are tied to the license key and unlocked instantly
- License is transferable (server owner sells their server, license goes with it)

---

## Platform Architecture

### High-Level Overview

```
┌─────────────────────────────────────────────────────────────┐
│                    CORROSION CLOUD                           │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌────────────────────┐  │
│  │  Web App     │  │  REST API   │  │  NATS Cluster      │  │
│  │  (Vue 3)     │  │  (Rust/Axum)│  │  (JetStream)       │  │
│  │             │  │             │  │                    │  │
│  │  Dashboard   │  │  Business   │  │  Real-time comms   │  │
│  │  Public sites│  │  Logic      │  │  Command delivery  │  │
│  │  Module store│  │  License    │  │  Stats streaming   │  │
│  │  Status page │  │  Billing    │  │  Health monitoring  │  │
│  └──────┬──────┘  └──────┬──────┘  └─────────┬──────────┘  │
│         │                │                    │             │
│  ┌──────┴────────────────┴────────────────────┴──────────┐  │
│  │                    PostgreSQL                          │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌───────────────┐  ┌──────────────┐  ┌────────────────┐   │
│  │  Cloudflare   │  │  Nginx       │  │  File Storage  │   │
│  │  DNS API      │  │  Reverse     │  │  (Maps, Backups│   │
│  │  (Subdomains) │  │  Proxy       │  │   Modules)     │   │
│  └───────────────┘  └──────────────┘  └────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │
                    NATS (outbound from customer)
                              │
          ┌───────────────────┼───────────────────┐
          │                   │                   │
   ┌──────┴──────┐    ┌──────┴──────┐    ┌──────┴──────┐
   │  Customer A │    │  Customer B │    │  Customer C │
   │             │    │             │    │             │
   │  Rust Server│    │  Rust Server│    │  Rust Server│
   │  + Plugin   │    │  + Plugin   │    │  + Plugin   │
   │  + Companion│    │  (AMP)      │    │  (Ptero)    │
   │  (Bare Metal│    │             │    │             │
   └─────────────┘    └─────────────┘    └─────────────┘
```

### Connection Model

All connections are **outbound from the customer's server** to the Corrosion cloud. No inbound ports need to be opened on the customer's firewall.

**Panel users (AMP / Pterodactyl):**
- uMod plugin connects outbound to Corrosion NATS cluster
- Customer provides panel API credentials in the dashboard
- Corrosion API calls the panel API directly for server control and file operations

**Bare metal users:**
- uMod plugin connects outbound to Corrosion NATS cluster
- Companion agent (pre-configured binary downloaded from dashboard) connects outbound to Corrosion NATS cluster
- Companion handles server process control and filesystem operations locally

**In both cases:** The customer's dashboard experience is identical. The connection method is an implementation detail they configure once during setup and never think about again.

---

## Technology Stack

### Corrosion Cloud — Backend (Rust)

| Component | Crate | Purpose |
|-----------|-------|---------|
| Web Framework | `axum` | HTTP API, built on Tokio async runtime |
| Database | `sqlx` | Compile-time verified PostgreSQL queries |
| Messaging | `async-nats` | NATS client with JetStream support |
| Middleware | `tower-http` | CORS, authentication, rate limiting, logging |
| Password Hashing | `argon2` | Secure credential storage |
| JWT Auth | `jsonwebtoken` | Session and API token management |
| 2FA | `totp-rs` | TOTP-based two-factor authentication |
| Email | `lettre` | Transactional email (verification, alerts, backup codes) |
| Serialization | `serde` / `serde_json` | JSON serialization/deserialization |
| Scheduling | `tokio-cron-scheduler` | Cron-based scheduled tasks (wipes, restarts, etc.) |
| HTTP Client | `reqwest` | Panel API calls, Cloudflare API, Steam API, PayPal webhooks |
| Encryption | `aes-gcm` | Encrypt sensitive stored data (API keys, webhook URLs) |
| URL Signing | `hmac` / `sha2` | Signed URLs for file downloads |

### Corrosion Cloud — Frontend (Vue 3 + TypeScript)

| Component | Library | Purpose |
|-----------|---------|---------|
| Build Tool | Vite | Fast dev server and production builds |
| Routing | Vue Router | SPA client-side routing |
| State | Pinia | Reactive state management |
| Styling | Tailwind CSS | Utility-first CSS |
| Charts | Apache ECharts | Analytics and monitoring visualizations |
| Real-time | NATS WebSocket | Live server data in dashboard |
| Icons | Lucide | Clean icon set |

### Game Server — uMod Plugin (C#)

| Component | Library | Purpose |
|-----------|---------|---------|
| Framework | Oxide/uMod | Rust game server plugin API |
| Messaging | NATS.Client | Outbound NATS connection to Corrosion cloud |
| JSON | Newtonsoft.Json | Payload serialization |
| HTTP | RestSharp | REST API calls (license validation, file downloads) |

### Game Server — Companion Agent (Go)

| Component | Purpose |
|-----------|---------|
| NATS Client | Outbound connection to Corrosion cloud |
| Process Manager | Start/stop/restart Rust server process |
| File System | Read/write/delete server files, map swaps, config edits |
| Steam Update | Trigger SteamCMD updates |
| Self-Update | Pull new versions from Corrosion API |

### Infrastructure

| Service | Purpose |
|---------|---------|
| PostgreSQL 16 | Primary data store (multi-tenant) |
| NATS Cluster (JetStream) | Real-time messaging, command delivery, stats streaming |
| Nginx | Reverse proxy, subdomain routing, static file serving |
| Cloudflare | DNS management (subdomain provisioning via API), SSL, CDN, DDoS protection |
| Object Storage (S3-compatible) | Map library, cloud backups, module distribution |

---

## Multi-Tenant Data Architecture

Every customer record is scoped by `license_id`. All queries filter by license. No customer can ever see another customer's data.

### Database Schema

#### `licenses`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_key | VARCHAR(64) | Unique, generated on CodeFling purchase |
| status | VARCHAR(20) | 'active', 'suspended', 'expired', 'revoked' |
| owner_user_id | UUID | FK → users (the purchaser) |
| server_name | VARCHAR(100) | Display name for this server |
| subdomain | VARCHAR(63) | Unique subdomain on corrosionmgmt.com |
| custom_domain | VARCHAR(255) | Optional CNAME domain |
| modules_enabled | TEXT[] | List of module slugs activated on this license |
| webstore_active | BOOLEAN | Default false |
| webstore_subscription_id | VARCHAR(100) | PayPal subscription ID if active |
| created_at | TIMESTAMPTZ | |
| expires_at | TIMESTAMPTZ | Nullable — lifetime licenses have no expiry |

#### `users`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| email | VARCHAR(255) | Unique, verified |
| username | VARCHAR(50) | Unique display name |
| password_hash | TEXT | Argon2 hashed |
| totp_secret | TEXT | Encrypted, nullable |
| totp_enabled | BOOLEAN | Default false |
| backup_codes | TEXT[] | Encrypted |
| email_verified | BOOLEAN | Default false |
| created_at | TIMESTAMPTZ | |
| last_login_at | TIMESTAMPTZ | |

#### `team_members`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| user_id | UUID | FK → users |
| role_id | UUID | FK → roles |
| invited_by | UUID | FK → users |
| accepted_at | TIMESTAMPTZ | Nullable until accepted |
| created_at | TIMESTAMPTZ | |

#### `roles`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses (null for system defaults) |
| role_name | VARCHAR(50) | e.g., "Owner", "Head Admin", "Moderator" |
| is_system_default | BOOLEAN | True for built-in roles |
| is_cloned_from | UUID | FK → roles, nullable |
| permissions | JSONB | Permission flags (see below) |
| created_at | TIMESTAMPTZ | |

**Default system roles:**

```json
{
  "owner": {
    "server.*": true,
    "wipe.*": true,
    "plugins.*": true,
    "players.*": true,
    "team.*": true,
    "billing.*": true,
    "store.*": true,
    "settings.*": true,
    "public_site.*": true
  },
  "head_admin": {
    "server.*": true,
    "wipe.*": true,
    "plugins.*": true,
    "players.*": true,
    "team.view": true,
    "team.invite": true,
    "store.*": true,
    "settings.server": true,
    "public_site.*": true
  },
  "moderator": {
    "server.view": true,
    "server.console": true,
    "players.kick": true,
    "players.ban": true,
    "players.chat_log": true,
    "wipe.view": true,
    "store.view": true
  },
  "viewer": {
    "server.view": true,
    "players.view": true,
    "wipe.view": true,
    "store.view": true
  }
}
```

#### `server_connections`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| connection_type | VARCHAR(20) | 'amp', 'pterodactyl', 'bare_metal' |
| panel_api_endpoint | TEXT | Nullable (not used for bare metal) |
| panel_api_key_encrypted | TEXT | Nullable |
| panel_server_identifier | VARCHAR(255) | Panel-specific server ID, nullable |
| companion_agent_token | VARCHAR(128) | Auth token for companion agent, nullable |
| companion_last_seen | TIMESTAMPTZ | Last heartbeat from companion |
| plugin_last_seen | TIMESTAMPTZ | Last heartbeat from uMod plugin |
| server_ip | VARCHAR(45) | |
| server_port | INTEGER | |
| game_port | INTEGER | |
| connection_status | VARCHAR(20) | 'connected', 'degraded', 'offline' |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |

#### `server_config`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| server_name | VARCHAR(255) | In-game server name |
| max_players | INTEGER | |
| world_size | INTEGER | |
| current_seed | INTEGER | |
| current_map_id | UUID | FK → map_library, nullable |
| server_description | TEXT | |
| server_url | TEXT | |
| server_header_image | TEXT | |
| tags | TEXT[] | Server tags |
| auto_restart_enabled | BOOLEAN | Default false |
| auto_restart_cron | VARCHAR(100) | Cron expression for scheduled restarts |
| auto_restart_timezone | VARCHAR(50) | |
| crash_recovery_enabled | BOOLEAN | Default true |
| crash_recovery_max_attempts | INTEGER | Default 3 |
| crash_recovery_cooldown_minutes | INTEGER | Default 10 |
| force_wipe_eligible | BOOLEAN | Default true |
| auto_update_on_force_wipe | BOOLEAN | Default true |
| config_overrides | JSONB | Additional server.cfg key-value pairs |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |

#### `game_admins`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| steam_id | VARCHAR(20) | Steam64 ID |
| display_name | VARCHAR(100) | |
| admin_level | VARCHAR(20) | 'owner', 'admin', 'moderator' |
| permissions | JSONB | Oxide permission flags |
| added_by | UUID | FK → users |
| created_at | TIMESTAMPTZ | |

#### `wipe_profiles`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| profile_name | VARCHAR(100) | |
| description | TEXT | |
| pre_wipe_config | JSONB | Pre-wipe sequence settings |
| post_wipe_config | JSONB | Post-wipe verification settings |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |

**`pre_wipe_config` JSONB structure:**

```json
{
  "enabled": true,
  "backup_before_wipe": true,
  "countdown_warnings": [30, 15, 5, 1],
  "countdown_unit": "minutes",
  "countdown_messages": {
    "30": "Server wipe in 30 minutes! Prepare yourselves!",
    "15": "Server wipe in 15 minutes!",
    "5": "Server wipe in 5 minutes! Wrapping up...",
    "1": "Server wipe in 1 minute! See you on the other side!"
  },
  "kick_players_before_wipe": true,
  "kick_message": "Server is wiping. Be back shortly!",
  "run_final_save": true,
  "discord_pre_announce": true,
  "pushbullet_notify": true,
  "custom_commands_before": []
}
```

**`post_wipe_config` JSONB structure:**

```json
{
  "enabled": true,
  "verify_server_started": true,
  "verify_correct_map": true,
  "verify_plugins_loaded": true,
  "verify_player_slots_open": true,
  "max_restart_attempts": 3,
  "health_check_timeout_seconds": 120,
  "discord_post_announce": true,
  "pushbullet_notify": true,
  "rollback_on_failure": true,
  "post_wipe_commands": []
}
```

#### `wipe_schedules`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| wipe_profile_id | UUID | FK → wipe_profiles |
| schedule_name | VARCHAR(100) | |
| wipe_type | VARCHAR(20) | 'map', 'blueprint', 'full' |
| cron_expression | VARCHAR(100) | |
| timezone | VARCHAR(50) | |
| wipe_blueprints | BOOLEAN | Default false |
| is_active | BOOLEAN | Default true |
| next_scheduled_run | TIMESTAMPTZ | |
| created_at | TIMESTAMPTZ | |

#### `map_library`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| filename | VARCHAR(255) | |
| display_name | VARCHAR(255) | |
| storage_path | TEXT | Path in object storage |
| file_size_bytes | BIGINT | |
| map_type | VARCHAR(20) | 'custom' or 'procedural' |
| seed | INTEGER | Nullable |
| world_size | INTEGER | Nullable |
| thumbnail_path | TEXT | Nullable |
| checksum | VARCHAR(64) | SHA-256 |
| uploaded_at | TIMESTAMPTZ | |

#### `map_rotations`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| map_id | UUID | FK → map_library |
| rotation_order | INTEGER | |
| is_active | BOOLEAN | Default true |

#### `plugin_registry`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| plugin_name | VARCHAR(255) | |
| plugin_version | VARCHAR(50) | |
| source | VARCHAR(20) | 'umod', 'corrosion_module', 'manual' |
| umod_slug | VARCHAR(255) | Nullable, for uMod-sourced plugins |
| is_installed | BOOLEAN | |
| is_loaded | BOOLEAN | Current runtime state from plugin report |
| config_json | JSONB | Plugin config (editable from dashboard) |
| data_path | TEXT | Relative path to plugin data |
| wipe_on_map | BOOLEAN | Default false |
| wipe_on_bp | BOOLEAN | Default false |
| wipe_on_full | BOOLEAN | Default false |
| never_wipe | BOOLEAN | Default false |
| installed_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |

#### `wipe_history`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| wipe_schedule_id | UUID | FK → wipe_schedules, nullable |
| wipe_profile_id | UUID | FK → wipe_profiles |
| wipe_type | VARCHAR(20) | |
| trigger_type | VARCHAR(20) | 'scheduled', 'manual', 'force_wipe' |
| status | VARCHAR(20) | 'pending', 'pre_wipe', 'wiping', 'post_wipe', 'success', 'failed', 'rolled_back' |
| started_at | TIMESTAMPTZ | |
| completed_at | TIMESTAMPTZ | |
| map_used | VARCHAR(255) | |
| plugins_wiped | TEXT[] | |
| plugins_preserved | TEXT[] | |
| backup_reference | TEXT | Storage path to pre-wipe backup |
| error_message | TEXT | |
| execution_log | JSONB | Step-by-step log |
| created_at | TIMESTAMPTZ | |

#### `scheduled_tasks`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| task_type | VARCHAR(30) | 'restart', 'announcement', 'command', 'plugin_reload' |
| task_name | VARCHAR(100) | |
| cron_expression | VARCHAR(100) | |
| timezone | VARCHAR(50) | |
| task_config | JSONB | Task-specific configuration |
| is_active | BOOLEAN | Default true |
| next_run | TIMESTAMPTZ | |
| created_at | TIMESTAMPTZ | |

#### `notifications_config`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| discord_webhook_url | TEXT | Encrypted |
| discord_enabled | BOOLEAN | Default false |
| pushbullet_api_key | TEXT | Encrypted |
| pushbullet_enabled | BOOLEAN | Default false |
| email_alerts_enabled | BOOLEAN | Default true |
| notify_wipe_start | BOOLEAN | Default true |
| notify_wipe_complete | BOOLEAN | Default true |
| notify_wipe_failed | BOOLEAN | Default true |
| notify_server_crash | BOOLEAN | Default true |
| notify_server_offline | BOOLEAN | Default true |
| notify_store_purchase | BOOLEAN | Default true |
| notify_player_report | BOOLEAN | Default false |
| created_at | TIMESTAMPTZ | |

#### `chat_logs`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| steam_id | VARCHAR(20) | Player Steam64 ID |
| player_name | VARCHAR(100) | |
| channel | VARCHAR(20) | 'global', 'team', 'server' |
| message | TEXT | |
| flagged | BOOLEAN | Default false |
| flagged_by | UUID | FK → users, nullable |
| flag_reason | TEXT | Nullable |
| created_at | TIMESTAMPTZ | Indexed |

#### `player_actions`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| steam_id | VARCHAR(20) | |
| player_name | VARCHAR(100) | |
| action_type | VARCHAR(20) | 'kick', 'ban', 'unban', 'warn', 'note' |
| reason | TEXT | |
| duration_minutes | INTEGER | Nullable (for timed bans) |
| performed_by | UUID | FK → users |
| created_at | TIMESTAMPTZ | |

#### `server_stats`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| player_count | INTEGER | |
| max_players | INTEGER | |
| fps | FLOAT | |
| entity_count | INTEGER | |
| uptime_seconds | INTEGER | |
| memory_usage_mb | INTEGER | |
| recorded_at | TIMESTAMPTZ | Indexed, partitioned by time |

#### `server_stats_hourly`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| hour | TIMESTAMPTZ | Truncated to hour |
| avg_players | FLOAT | |
| max_players | INTEGER | |
| avg_fps | FLOAT | |
| min_fps | FLOAT | |
| avg_entities | INTEGER | |
| uptime_percentage | FLOAT | |

#### `public_site_config`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| site_enabled | BOOLEAN | Default true |
| show_on_status_page | BOOLEAN | Default false |
| steam_connect_url | VARCHAR(255) | |
| motd | TEXT | |
| public_mods | TEXT[] | Curated list of plugins to show |
| header_image_url | TEXT | |
| theme_color | VARCHAR(7) | Hex color |
| custom_css | TEXT | Nullable |
| discord_invite_url | TEXT | |
| show_player_count | BOOLEAN | Default true |
| show_wipe_schedule | BOOLEAN | Default true |
| show_wipe_countdown | BOOLEAN | Default true |
| show_mod_list | BOOLEAN | Default true |
| created_at | TIMESTAMPTZ | |

#### `webstore_config`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| is_active | BOOLEAN | Default false |
| paypal_client_id | TEXT | Encrypted |
| paypal_secret | TEXT | Encrypted |
| paypal_mode | VARCHAR(10) | 'sandbox' or 'live' |
| store_name | VARCHAR(100) | |
| store_description | TEXT | |
| currency | VARCHAR(3) | Default 'USD' |
| created_at | TIMESTAMPTZ | |

#### `webstore_categories`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| category_name | VARCHAR(100) | |
| display_order | INTEGER | |
| is_active | BOOLEAN | Default true |

#### `webstore_items`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| category_id | UUID | FK → webstore_categories |
| item_name | VARCHAR(100) | |
| description | TEXT | |
| price | DECIMAL(10,2) | |
| image_url | TEXT | |
| item_type | VARCHAR(20) | 'kit', 'rank', 'currency', 'custom_command' |
| delivery_config | JSONB | Commands to execute on delivery |
| is_active | BOOLEAN | Default true |
| created_at | TIMESTAMPTZ | |

#### `webstore_transactions`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| item_id | UUID | FK → webstore_items |
| buyer_steam_id | VARCHAR(20) | |
| buyer_name | VARCHAR(100) | |
| amount | DECIMAL(10,2) | |
| currency | VARCHAR(3) | |
| paypal_transaction_id | VARCHAR(100) | |
| status | VARCHAR(20) | 'pending', 'paid', 'delivered', 'failed', 'refunded' |
| delivered_at | TIMESTAMPTZ | Nullable |
| created_at | TIMESTAMPTZ | |

#### `migration_exports`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| license_id | UUID | FK → licenses |
| export_type | VARCHAR(20) | 'full', 'config_only', 'store_only' |
| storage_path | TEXT | Path to export archive |
| file_size_bytes | BIGINT | |
| created_by | UUID | FK → users |
| expires_at | TIMESTAMPTZ | Auto-delete after 7 days |
| created_at | TIMESTAMPTZ | |

#### `platform_changelog`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| version | VARCHAR(20) | |
| title | VARCHAR(200) | |
| body | TEXT | Markdown |
| category | VARCHAR(20) | 'feature', 'bugfix', 'module', 'security' |
| published_at | TIMESTAMPTZ | |

#### `module_store`

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| module_slug | VARCHAR(50) | Unique identifier |
| module_name | VARCHAR(100) | Display name |
| description | TEXT | |
| long_description | TEXT | Markdown |
| price | DECIMAL(10,2) | One-time price |
| price_type | VARCHAR(20) | 'one_time' or 'monthly' |
| monthly_price | DECIMAL(10,2) | Nullable |
| version | VARCHAR(20) | |
| download_path | TEXT | |
| thumbnail_url | TEXT | |
| screenshots | TEXT[] | |
| category | VARCHAR(50) | |
| is_active | BOOLEAN | Default true |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |

---

## NATS Subject Architecture

All subjects are namespaced by license ID for multi-tenant isolation.

### Core Subjects

| Subject | Direction | Payload | Purpose |
|---------|-----------|---------|---------|
| `corrosion.{license_id}.heartbeat` | Plugin → Cloud | `{ timestamp, status, player_count, fps }` | Plugin health check (every 30s) |
| `corrosion.{license_id}.companion.heartbeat` | Companion → Cloud | `{ timestamp, status, disk_free, cpu }` | Companion health check (every 30s) |
| `corrosion.{license_id}.stats` | Plugin → Cloud | `{ players, fps, entities, uptime, memory }` | Server metrics (every 60s) |
| `corrosion.{license_id}.players.event` | Plugin → Cloud | `{ event, steam_id, name, ip }` | Player join/leave/death events |
| `corrosion.{license_id}.chat` | Plugin → Cloud | `{ steam_id, name, channel, message }` | Chat messages for logging |
| `corrosion.{license_id}.console` | Plugin → Cloud | `{ line, level, timestamp }` | Server console output stream |

### Command Subjects (Cloud → Game Server)

| Subject | Direction | Payload | Purpose |
|---------|-----------|---------|---------|
| `corrosion.{license_id}.cmd.server` | Cloud → Companion | `{ action: start/stop/restart }` | Server process control |
| `corrosion.{license_id}.cmd.console` | Cloud → Plugin | `{ command }` | Execute server command |
| `corrosion.{license_id}.cmd.console.response` | Plugin → Cloud | `{ command, output }` | Command result |
| `corrosion.{license_id}.cmd.announce` | Cloud → Plugin | `{ message }` | In-game broadcast |
| `corrosion.{license_id}.cmd.kick` | Cloud → Plugin | `{ steam_id, reason }` | Kick player |
| `corrosion.{license_id}.cmd.ban` | Cloud → Plugin | `{ steam_id, reason, duration }` | Ban player |
| `corrosion.{license_id}.cmd.plugin` | Cloud → Plugin | `{ action: load/unload/reload, name }` | Plugin management |
| `corrosion.{license_id}.cmd.deliver` | Cloud → Plugin | `{ steam_id, commands[] }` | Webstore item delivery |

### Wipe Subjects

| Subject | Direction | Payload | Purpose |
|---------|-----------|---------|---------|
| `corrosion.{license_id}.wipe.prepare` | Cloud → Plugin | `{ wipe_id, countdown_config }` | Start pre-wipe countdown |
| `corrosion.{license_id}.wipe.status` | Plugin → Cloud | `{ wipe_id, step, progress, message }` | Wipe progress updates |
| `corrosion.{license_id}.wipe.health` | Plugin → Cloud | `{ map, seed, plugins[], fps, slots }` | Post-wipe health report |

### File Operation Subjects (Cloud → Companion)

| Subject | Direction | Payload | Purpose |
|---------|-----------|---------|---------|
| `corrosion.{license_id}.files.get` | Cloud → Companion | `{ path }` | Read file from server |
| `corrosion.{license_id}.files.put` | Cloud → Companion | `{ path, download_url }` | Write file to server |
| `corrosion.{license_id}.files.delete` | Cloud → Companion | `{ path }` | Delete file from server |
| `corrosion.{license_id}.files.list` | Cloud → Companion | `{ path }` | List directory contents |
| `corrosion.{license_id}.files.response` | Companion → Cloud | `{ request_id, data/status }` | File operation result |
| `corrosion.{license_id}.update.steam` | Cloud → Companion | `{ validate: bool }` | Trigger SteamCMD update |

### JetStream Streams

| Stream | Subjects | Retention | Purpose |
|--------|----------|-----------|---------|
| HEARTBEATS | `corrosion.*.heartbeat`, `corrosion.*.companion.heartbeat` | Limits (1 hour) | Connection monitoring |
| STATS | `corrosion.*.stats` | Limits (24 hours) | Raw metrics for aggregation |
| EVENTS | `corrosion.*.players.event` | Limits (7 days) | Player events for analytics |
| CHAT | `corrosion.*.chat` | Limits (30 days) | Chat log retention |
| COMMANDS | `corrosion.*.cmd.*` | WorkQueue | Reliable command delivery |
| WIPE_EVENTS | `corrosion.*.wipe.*` | Limits (30 days) | Wipe audit trail |

---

## Wipe Execution Sequence

**CRITICAL DESIGN PRINCIPLE:** All file system operations are performed by the **Corrosion Cloud API** — either via the Panel API (AMP/Pterodactyl) or via the Companion Agent (bare metal) — while the server is **stopped**. The C# plugin handles only in-game operations and post-restart verification.

```
WIPE SEQUENCE
│
├── 1. PRE-WIPE [Plugin — server is RUNNING]
│   ├── Cloud sends wipe.prepare with countdown config
│   ├── Plugin broadcasts in-game countdown warnings
│   ├── Cloud sends Discord/Pushbullet pre-wipe notification
│   ├── Plugin executes custom pre-wipe commands
│   ├── Plugin kicks all players with configured message
│   ├── Plugin confirms all players disconnected
│   ├── Cloud triggers final server save (via console command)
│   └── Cloud stops server (via Panel API or Companion)
│
├── 2. WIPE EXECUTION [Cloud — server is STOPPED]
│   ├── Create backup (via Panel API or Companion file ops)
│   ├── Delete selected plugin data files per wipe settings
│   ├── Preserve protected plugin data (never_wipe = true)
│   ├── Handle blueprint wipe if configured
│   ├── Rotate to next map in rotation
│   │   ├── Custom map: transfer from Corrosion storage to server
│   │   └── Procedural: update seed/size values
│   ├── Update server.cfg with new map settings
│   └── Log every action to wipe_history
│
├── 3. SERVER RESTART [Cloud]
│   ├── If force wipe: trigger SteamCMD update first
│   ├── Start server (via Panel API or Companion)
│   └── Wait for plugin heartbeat on NATS (confirms server is up)
│
├── 4. POST-WIPE VERIFICATION [Plugin — server is RUNNING]
│   ├── Plugin publishes health report via NATS
│   ├── Cloud evaluates health against expected state
│   ├── Retry up to max_restart_attempts on failure
│   └── If all retries fail + rollback enabled:
│       └── Cloud executes rollback (stop → restore backup → start)
│
├── 5. POST-WIPE NOTIFICATION [Cloud]
│   ├── Update wipe_history record
│   ├── Discord/Pushbullet: wipe complete or failed
│   ├── Send in-game announcement via plugin
│   └── Log final status
│
└── DONE
```

---

## Steam Update Watcher

Background service in the Corrosion Cloud API that monitors for Rust Dedicated Server updates.

```
STEAM UPDATE WATCHER
│
├── Poll Steam Web API every 60 seconds
│   └── Check buildid for app 258550 (Rust Dedicated Server)
│
├── On buildid change detected:
│   ├── For each license with auto_update_on_force_wipe = true:
│   │   ├── If force_wipe_eligible: trigger full wipe sequence
│   │   └── If not: trigger update + restart only
│   └── Send notifications to all affected license holders
│
└── Store detected force wipe dates for calendar display
```

---

## Customer Onboarding Flow (Setup Wizard)

```
REGISTRATION & SETUP
│
├── 1. REGISTER
│   ├── Customer visits panel.corrosionmgmt.com/register
│   ├── Enters: email, username, password, license key
│   ├── Email verification sent
│   ├── License key validated against CodeFling purchase
│   └── Account created, redirected to setup wizard
│
├── 2. SETUP WIZARD — Step 1: Name Your Server
│   ├── Server display name
│   └── Choose subdomain: _______.corrosionmgmt.com
│       └── Cloudflare API creates DNS record immediately
│
├── 3. SETUP WIZARD — Step 2: How Is Your Server Hosted?
│   ├── Option A: Game Hosting Panel (AMP)
│   │   └── Enter AMP API endpoint + credentials → test connection
│   ├── Option B: Game Hosting Panel (Pterodactyl)
│   │   └── Enter Pterodactyl API endpoint + credentials → test connection
│   └── Option C: Bare Metal / Other
│       └── Download pre-configured companion agent
│
├── 4. SETUP WIZARD — Step 3: Install the Plugin
│   ├── Download WipeManager.cs (pre-configured with license token)
│   ├── Instructions: "Copy to oxide/plugins/ and restart your server"
│   └── Dashboard shows "Waiting for plugin connection..."
│       └── Plugin connects → green checkmark → "Server Online!"
│
├── 5. SETUP WIZARD — Step 4: Server Template (Optional)
│   ├── What kind of server are you running?
│   │   ├── Vanilla
│   │   ├── 2x Modded
│   │   ├── 5x Modded
│   │   ├── 10x Modded
│   │   ├── PvE
│   │   ├── Creative/Build
│   │   └── Custom (skip templates)
│   ├── Selected template auto-installs recommended plugins from uMod
│   └── Plugins installed with sensible default configs
│
├── 6. SETUP WIZARD — Step 5: Set Up Your First Wipe Schedule
│   ├── How often do you wipe? (weekly/bi-weekly/monthly)
│   ├── What day and time?
│   ├── Wipe blueprints? (yes/no)
│   └── Creates default wipe profile and schedule
│
└── 7. DONE → Dashboard
    └── "Your server is live at servername.corrosionmgmt.com!"
```

---

## Subdomain & Routing Architecture

### URL Structure

| URL | Purpose |
|-----|---------|
| `panel.corrosionmgmt.com` | Main platform login and dashboard |
| `{servername}.corrosionmgmt.com` | Customer's public server site |
| `{servername}.corrosionmgmt.com/store` | Customer's webstore (if enabled) |
| `status.corrosionmgmt.com` | Public status page (opted-in servers) |
| `api.corrosionmgmt.com` | REST API endpoint |
| `nats.corrosionmgmt.com` | NATS cluster endpoint (plugin/companion connections) |

### Subdomain Provisioning

On license registration:
1. Customer chooses subdomain during setup wizard
2. Corrosion API calls Cloudflare API to create CNAME/A record
3. Nginx config dynamically routes based on subdomain → license_id lookup
4. SSL handled automatically by Cloudflare

For custom domains:
1. Customer adds CNAME record pointing their domain to `{servername}.corrosionmgmt.com`
2. Customer enters custom domain in dashboard settings
3. Corrosion API updates Cloudflare and Nginx routing
4. SSL provisioned via Cloudflare

---

## Security Model

### Authentication & Authorization

- User registration with email verification
- Argon2 password hashing
- JWT access tokens (15 min expiry) + refresh tokens (7 days)
- TOTP 2FA with encrypted backup codes
- RBAC enforced on every API endpoint
- License ownership verified on all license-scoped operations

### Data Isolation

- Every database query filters by `license_id`
- NATS subjects namespaced by `license_id` — subscriptions scoped per tenant
- File storage paths prefixed by `license_id`
- API middleware validates license ownership before any operation

### Secrets Management

- Panel API keys encrypted at rest (AES-256-GCM)
- Discord webhook URLs encrypted at rest
- Pushbullet API keys encrypted at rest
- PayPal credentials encrypted at rest
- Companion agent tokens are unique per license, rotatable from dashboard

### Plugin & Companion Authentication

- Plugin authenticates to NATS with license-specific token
- Companion authenticates to NATS with separate companion-specific token
- Both tokens are revocable from the dashboard
- License validation on plugin startup and periodic check-in (every 6 hours)
- Invalid/expired license → plugin enters read-only mode (stats only, no commands)

---

## Crash Recovery System

```
CRASH DETECTION
│
├── Plugin heartbeat stops (no message for 90 seconds)
│   └── Cloud marks server as "unresponsive"
│
├── Companion heartbeat confirms server process is dead
│   └── OR Panel API confirms server is stopped
│
├── If crash_recovery_enabled = true:
│   ├── Attempt 1: Restart server
│   │   └── Wait for plugin heartbeat (timeout: 120s)
│   ├── Attempt 2: Restart server
│   │   └── Wait for plugin heartbeat (timeout: 120s)
│   ├── Attempt 3: Restart server
│   │   └── Wait for plugin heartbeat (timeout: 120s)
│   └── All attempts failed:
│       ├── Mark server as "crashed — manual intervention required"
│       └── Send critical alert (Discord + Pushbullet + Email)
│
├── Cooldown: No more auto-recovery for crash_recovery_cooldown_minutes
│   └── Prevents restart loops from corrupted data
│
└── All crash events logged with timestamps and attempt details
```

---

## Server Migration System

### Export

Customer triggers export from dashboard. System packages:

- All server configuration (server_config, config_overrides)
- All wipe profiles and schedules
- Plugin registry with configurations
- Map rotation settings (not map files — too large)
- Webstore categories and items (not transaction history)
- Public site configuration
- Game admin list
- Notification settings
- RBAC roles (custom only)

Exported as encrypted JSON archive. Download link valid for 7 days.

### Import

Customer registers new license, runs setup wizard, then imports:

- Upload export archive
- System validates and maps data to new license_id
- Customer reviews what will be imported
- One-click apply
- Map files must be re-uploaded separately

---

# PHASE 1 — Foundation & Core Platform

**Goal:** Customer buys the plugin, registers, connects their server, and sees it live on the dashboard with basic management capabilities. Prove the platform works end-to-end.

**Success metric:** A customer with zero technical knowledge beyond "install this plugin" can register, connect their server, and manage it from the web within 15 minutes.

### Phase 1 Deliverables

**Platform Infrastructure**

- [ ] Corrosion Cloud deployment (Docker Compose: PostgreSQL, NATS, Rust API, Nginx)
- [ ] `panel.corrosionmgmt.com` — main web application
- [ ] Cloudflare DNS API integration for subdomain provisioning
- [ ] Nginx dynamic subdomain routing
- [ ] SSL via Cloudflare (wildcard cert for *.corrosionmgmt.com)

**Registration & Licensing**

- [ ] User registration with email verification
- [ ] License key validation and activation
- [ ] Login with JWT auth
- [ ] TOTP 2FA setup and verification
- [ ] License check-in endpoint for plugin validation

**Setup Wizard**

- [ ] Step 1: Name server + choose subdomain
- [ ] Step 2: Select connection type (AMP / Pterodactyl / Bare Metal)
- [ ] Step 3: Panel API configuration with connection test OR companion agent download
- [ ] Step 4: Plugin download (pre-configured with license token) + connection verification
- [ ] Step 5: Server template selection with auto plugin install from uMod
- [ ] Step 6: First wipe schedule creation
- [ ] Wizard completion → redirect to dashboard

**uMod Plugin (C#)**

- [ ] NATS outbound connection with auto-reconnect
- [ ] License token authentication
- [ ] Periodic license validation (every 6 hours)
- [ ] Heartbeat publishing (every 30 seconds)
- [ ] Server stats publishing (players, FPS, entities, uptime, memory)
- [ ] Player join/leave event publishing
- [ ] Chat message publishing
- [ ] Console output streaming
- [ ] Command subscription and execution
- [ ] Player kick/ban execution
- [ ] In-game announcement broadcasting
- [ ] Plugin list reporting (name, version, loaded state)
- [ ] Wipe countdown message broadcasting
- [ ] Post-wipe health report publishing
- [ ] Configurable via license token only (no manual config editing)

**Companion Agent (Go)**

- [ ] NATS outbound connection with auto-reconnect
- [ ] Pre-configured download from dashboard (license token + NATS endpoint baked in)
- [ ] Server process management (start, stop, restart)
- [ ] Server process monitoring (detect crashes)
- [ ] File system operations (read, write, delete, list)
- [ ] SteamCMD update execution
- [ ] Self-update capability
- [ ] Runs as system service (systemd / Windows service)

**Panel Adapters**

- [ ] PanelAdapter trait definition
- [ ] AMP adapter: connect, test, discover server, start, stop, restart, file ops, send command
- [ ] Pterodactyl adapter: same interface
- [ ] Panel connection health monitoring

**Dashboard — Server Management**

- [ ] Server status overview (online/offline, player count, FPS, uptime)
- [ ] Start / Stop / Restart server
- [ ] Live server console (real-time via NATS WebSocket)
- [ ] Console command input with history
- [ ] Server configuration editor (server.cfg key-value editing from web)
- [ ] Scheduled restarts (cron-based with timezone)
- [ ] Crash recovery (auto-restart with configurable attempts and cooldown)

**Dashboard — Player Management**

- [ ] Live player list (name, SteamID, playtime, ping)
- [ ] Kick player with reason
- [ ] Ban player with reason and duration
- [ ] Unban player
- [ ] Ban list management
- [ ] In-game admin registration (enter SteamID, select level, apply)

**Dashboard — Plugin Management**

- [ ] View installed plugins with version info
- [ ] Load / Unload / Reload plugins
- [ ] uMod plugin browser (search and install from uMod)
- [ ] Plugin configuration editing from web UI (JSON editor with save)
- [ ] Plugin data wipe settings (per-plugin, per-wipe-type toggles)

**Dashboard — Auto-Wiper**

- [ ] Wipe profile creation and management
- [ ] Pre-wipe sequence configuration (toggleable steps)
- [ ] Post-wipe verification configuration (toggleable checks)
- [ ] Wipe schedule creation (cron with timezone, wipe type)
- [ ] Wipe calendar view (all scheduled wipes + force wipe dates)
- [ ] Facepunch force wipe conflict detection
- [ ] SteamUpdateWatcher (buildid polling, auto-trigger)
- [ ] Manual wipe trigger with confirmation
- [ ] Dry-run / preview mode
- [ ] Wipe history with detailed execution logs
- [ ] Pre-wipe backup (via panel/companion)
- [ ] One-click rollback from dashboard

**Dashboard — Map Management**

- [ ] Map library (upload, metadata, thumbnails)
- [ ] Map rotation configuration (ordered list per server)
- [ ] Procedural seed management
- [ ] Signed download URLs for plugin map retrieval (15 min expiry)
- [ ] Checksum verification

**Dashboard — Notifications**

- [ ] Discord webhook configuration and test
- [ ] Pushbullet configuration and test
- [ ] Email alerts (server crash, wipe failed)
- [ ] Per-event notification toggles

**Dashboard — Team Management (RBAC)**

- [ ] Invite team members by email
- [ ] Assign roles to team members
- [ ] Default system roles (Owner, Head Admin, Moderator, Viewer)
- [ ] Clone and customize roles
- [ ] Permission enforcement on all UI elements and API endpoints

**Dashboard — Chat Log & Moderation**

- [ ] Real-time chat feed from server
- [ ] Chat history with search
- [ ] Flag messages with reason
- [ ] Quick-action: kick/ban from chat log entry

**Public Server Site (`servername.corrosionmgmt.com`)**

- [ ] Server info display (name, description, header image)
- [ ] Steam connect link (clickable)
- [ ] MOTD display
- [ ] Mod/plugin list (curated by admin)
- [ ] Wipe schedule display
- [ ] Next wipe countdown timer (live via NATS WebSocket)
- [ ] Live player count
- [ ] Discord invite link
- [ ] Configurable from dashboard (toggle sections on/off, colors, images)

**Platform Features**

- [ ] Changelog / update feed in dashboard
- [ ] Server migration export
- [ ] Server migration import

---

# PHASE 2 — Analytics & Intelligence

**Goal:** Data-driven insights that make admins smarter and look incredible in screenshots. This is the CodeFling differentiator.

**Depends on:** Phase 1 complete. NATS stats stream already collecting data.

### Phase 2 Deliverables

**Player Analytics**

- [ ] Player count tracking over time (hourly, daily, weekly graphs)
- [ ] Peak player tracking per day/week/month
- [ ] Player retention after wipe (what percentage return within 24h, 48h, 72h)
- [ ] Player count correlation with wipe timing
- [ ] Unique player counts (SteamID-based)
- [ ] Average session duration trends
- [ ] New vs returning player ratio
- [ ] Player join/leave heatmap (time of day patterns)

**Wipe Analytics**

- [ ] Wipe success/failure rate over time
- [ ] Average wipe execution duration
- [ ] Wipe-to-peak-population time (how fast server fills post-wipe)
- [ ] Population curve by wipe cycle (day 1 vs day 2 vs day 3 pattern)
- [ ] Optimal wipe timing recommendations based on historical data
- [ ] "Your best wipe day is X at Y" summary card

**Server Performance Analytics**

- [ ] FPS tracking over time (trend graphs)
- [ ] Entity count growth over wipe cycle
- [ ] FPS vs player count correlation
- [ ] Memory usage trends
- [ ] Performance degradation alerts (FPS below configurable threshold)

**Map Analytics**

- [ ] Player count per map (which maps drive the most players)
- [ ] Average population per map across wipe cycles
- [ ] Map rotation effectiveness scoring

**Dashboard Visualizations (Apache ECharts)**

- [ ] Interactive charts with date range selectors
- [ ] Server comparison views (if customer has multiple licenses)
- [ ] Summary insight cards
- [ ] Real-time updating charts
- [ ] Exportable chart images (PNG download for CodeFling screenshots / social media)

**Alerting**

- [ ] Discord/Pushbullet alerts for anomalies (population drop > X%, FPS below threshold)
- [ ] Configurable alert thresholds
- [ ] Alert history log

**Data Infrastructure**

- [ ] Aggregation service: raw stats → hourly → daily rollups
- [ ] Data retention policies (raw: 7 days, hourly: 90 days, daily: 1 year)
- [ ] Historical data CSV export

---

# PHASE 3 — Status Page

**Goal:** Public status page showcasing all opted-in servers. Free marketing for the platform.

**Depends on:** Phase 1 complete. Heartbeat and stats data already flowing.

### Phase 3 Deliverables

**Status Page (`status.corrosionmgmt.com`)**

- [ ] Public page listing all servers with `show_on_status_page = true`
- [ ] Per-server: name, status (online/offline/degraded), player count, uptime percentage
- [ ] Per-server: basic config display (map size, max players, wipe schedule)
- [ ] Overall platform health summary
- [ ] Uptime history graph per server (24h, 7d, 30d)
- [ ] Incident history (downtime events with duration)
- [ ] Real-time updates via NATS WebSocket
- [ ] Mobile-responsive design
- [ ] "Powered by Corrosion" branding with link to platform

---

# PHASE 4 — Module Store & First Modules

**Goal:** Launch the module marketplace and ship the first paid add-on modules. Prove the module ecosystem works.

**Depends on:** Phase 1 complete. Plugin infrastructure supports module loading.

### Phase 4 Deliverables

**Module Store Infrastructure**

- [ ] Module store page in dashboard (browse, preview, purchase)
- [ ] Module purchase flow (payment processing — details TBD)
- [ ] License key module activation (purchased modules tied to license)
- [ ] Module auto-installation (download module plugin, deploy to server via companion/panel)
- [ ] Module update system (new versions auto-detected, one-click update)
- [ ] Module configuration in dashboard (per-module settings UI)

**Loot Manager Module ($9.99)**

- [ ] Visual loot table editor (drag-and-drop items, set spawn rates, quantities)
- [ ] Container browser (every crate type, barrel, NPC drop, Bradley, Heli)
- [ ] Loot profiles ("2x Vanilla", "10x Modded", "Event Weekend")
- [ ] One-click profile switching (no restart required)
- [ ] Import/export loot profiles
- [ ] Skin selection from visual gallery
- [ ] Probability distribution preview
- [ ] Wipe-aware loot rotation (swap loot profiles automatically on wipe day)
- [ ] Scheduled loot changes (generous day 1, tighter day 5)

**Future Module Pipeline (not built in Phase 4, but planned)**

- Event plugins (convoy-style PvE events)
- Economics/currency system
- Kits manager
- Teleportation system
- Home/base management
- Clan/team management
- Voting rewards
- Custom crafting recipes

---

# PHASE 5 — Integrated Webstore

**Goal:** Tebex alternative where server owners monetize their community. $10/month recurring revenue per customer.

**Depends on:** Phase 1 complete (public site), Phase 4 complete (module delivery infrastructure).

### Phase 5 Deliverables

**Webstore Setup**

- [ ] Webstore add-on activation ($10/month subscription via PayPal)
- [ ] PayPal Business account connection (client ID + secret)
- [ ] Sandbox mode for testing before going live
- [ ] Store configuration (name, description, currency)

**Store Management (Dashboard)**

- [ ] Category management (create, reorder, toggle)
- [ ] Item management (create, edit, pricing, images)
- [ ] Item types: kits, ranks, in-game currency, custom commands
- [ ] Delivery configuration (server commands to execute on purchase)
- [ ] Item preview (see what the customer sees)

**Customer-Facing Store (`servername.corrosionmgmt.com/store`)**

- [ ] Store page integrated into public server site
- [ ] Category browsing
- [ ] Item detail pages with images and descriptions
- [ ] Cart and checkout flow
- [ ] PayPal payment processing (money goes directly to server owner)
- [ ] SteamID verification (buyer enters their Steam ID)
- [ ] Order confirmation page

**Delivery System**

- [ ] PayPal IPN/webhook listener
- [ ] Payment verification
- [ ] Automatic in-game delivery via NATS → Plugin command execution
- [ ] Delivery for online players: immediate
- [ ] Delivery for offline players: queued, delivered on next join
- [ ] Delivery confirmation logging

**Revenue Dashboard**

- [ ] Transaction history with filters
- [ ] Revenue summary (daily, weekly, monthly)
- [ ] Top-selling items
- [ ] Purchase activity graph
- [ ] Transaction status tracking (pending, paid, delivered, failed)

**Store Notifications**

- [ ] Discord notification on purchase
- [ ] Pushbullet notification on purchase
- [ ] Email receipt to buyer (optional)
- [ ] In-game announcement on purchase (optional, "Player X just bought VIP!")

---

# PHASE 6 — B2B Site Licensing

**Goal:** Hosting companies integrate Corrosion into their platform as a value-add for their Rust customers. High-value recurring B2B revenue.

**Depends on:** All previous phases stable and proven in production.

### Phase 6 Deliverables

**B2B Infrastructure**

- [ ] Organization/tenant model (hosting company as parent org)
- [ ] Bulk license provisioning (hosting company generates licenses for their customers)
- [ ] White-label options (custom branding, custom domain for the panel)
- [ ] Usage-based billing (per-active-server-month)
- [ ] B2B admin dashboard (hosting company sees all their customer servers)
- [ ] Custom panel adapter development support (hosting company's proprietary panel)

**Integration**

- [ ] API for hosting company to provision/deprovision servers
- [ ] SSO integration (customer logs into hosting company panel → auto-authenticated in Corrosion)
- [ ] Embedded dashboard option (iframe or SDK for embedding in hosting company panel)
- [ ] Custom module distribution (hosting company can offer their own modules)

---

## Infrastructure Scaling Notes

### Phase 1 (Single-server deployment)

```yaml
# Docker Compose — suitable for initial launch
services:
  postgres:
    image: postgres:16-alpine
    volumes: [pg_data:/var/lib/postgresql/data]

  nats:
    image: nats:latest
    command: [--jetstream, --store_dir, /data, --config, /etc/nats/nats.conf]
    volumes: [nats_data:/data]
    ports: [4222:4222, 9222:9222]  # Client + WebSocket

  api:
    build: ./backend
    environment:
      DATABASE_URL: postgres://...
      NATS_URL: nats://nats:4222
      CLOUDFLARE_API_TOKEN: ${CF_TOKEN}
      CLOUDFLARE_ZONE_ID: ${CF_ZONE}
      ENCRYPTION_KEY: ${ENC_KEY}

  nginx:
    image: nginx:alpine
    ports: [80:80, 443:443]
    volumes:
      - ./frontend/dist:/usr/share/nginx/html
      - ./nginx.conf:/etc/nginx/nginx.conf
```

### Scaling Milestones

| Customers | Infrastructure Change |
|-----------|----------------------|
| 1–50 | Single Docker Compose on one VPS |
| 50–200 | Separate database server, NATS on dedicated node |
| 200–500 | NATS cluster (3 nodes), read replicas for PostgreSQL |
| 500+ | Kubernetes, horizontal API scaling, managed PostgreSQL |

The architecture supports all of these transitions without application code changes. NATS clustering is transparent to clients. Database connection pooling handles read replicas. API is stateless (JWT + NATS) so it scales horizontally.

---

## Revenue Projections (Conservative)

| Milestone | Customers | Monthly Revenue |
|-----------|-----------|-----------------|
| Launch + 6 months | 50 licenses ($50 each) | $2,500 one-time + module sales |
| Year 1 | 200 licenses | ~$10,000 one-time + $500/mo webstore subscriptions |
| Year 2 | 500 licenses, 2 hosting companies | ~$25,000 one-time + $2,000/mo recurring |
| Year 3 | 1,000+ licenses, 5 hosting companies | Recurring revenue exceeds one-time |

These are conservative. The Convoy plugin sold 29,000 copies at $35. The Rust server market is large and underserved for management tooling.

---

*End of specification. To begin implementation, say: "Build Phase X"*
