From 1935a46822c527d9b4167fe0825a29848e6a63d3 Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Sat, 14 Feb 2026 21:29:04 -0500 Subject: [PATCH] docs: Initial architecture spec for Corrosion platform --- docs/corrosion-architecture.md | 1444 ++++++++++++++++++++++++++++++++ 1 file changed, 1444 insertions(+) create mode 100644 docs/corrosion-architecture.md diff --git a/docs/corrosion-architecture.md b/docs/corrosion-architecture.md new file mode 100644 index 0000000..5485bed --- /dev/null +++ b/docs/corrosion-architecture.md @@ -0,0 +1,1444 @@ +# 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 (Rust or 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 (Rust or 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"*