Перейти к содержанию

Remnawave

Updated: Mar 2026 Status: 25 nodes deployed, backend integration live, config delivery in PR


Quick Facts

Panel URL https://market-plus.vip/ (nginx on 212.70.189.60:443 → 10.99.87.249:3000)
Admin creds Vaultwarden → Infrastructure → Remnawave Panel
Nodes 25 active, xray 25.12.8, VLESS+TCP+Reality, port 443
Subscription URL https://market-plus.vip/api/sub/{shortUuid}
Node container remnanode (network_mode: host, port 2222 mTLS)
Replaced Hiddify (7 Feb 2026) — haproxy + PROXY protocol v2 broke Streisand iOS Reality handshake

1. Subscription URL

Structure

https://market-plus.vip/api/sub/{shortUuid}
  • market-plus.vip — neutral domain (not VPN-branded), proxied through 3 API proxies (DE/AT/NL)
  • shortUuid — 36-char user identifier stored in account.remna_short_uuid
  • Public endpoint — no authentication required

Response Format

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Encoding: base64

dmxlc3M6Ly81ZDM2MjZmNC1jYzk5LTQ3YzctOTE4YS1hZTQ4MjM5ZmRhZWRAMTMwLjE5NS4yMjIu...

Decoded — one VLESS URL per line, 25 total:

vless://UUID@130.195.222.205:443?encryption=none&flow=xtls-rprx-vision&reality=1&pbk=OISHrR3ONyKjXNjVv277fMwBt28kjEARIgFWftG-Rkk&sid=&sni=dl.google.com&fp=chrome&type=tcp&host=dl.google.com#AT-Vienna-Reality

vless://UUID@146.70.121.16:443?encryption=none&flow=xtls-rprx-vision&reality=1&pbk=OISHrR3ONyKjXNjVv277fMwBt28kjEARIgFWftG-Rkk&sid=&sni=dl.google.com&fp=chrome&type=tcp&host=dl.google.com#GB-Manchester-02-Reality

Key properties: - One subscription URL covers all 25 nodes - Base64 encoding (standard subscription format for V2rayNG/Streisand/Hiddify Next) - Remnawave tracks usedTrafficBytes per user - Client apps typically re-poll every hour

User Model in Remnawave

{
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "shortUuid": "550e8400-e29b-41d4",
  "username": "1234567890123456",      // account.account (16-digit)
  "status": "ACTIVE",
  "trafficLimitBytes": 0,              // 0 = unlimited
  "trafficLimitStrategy": "NO_RESET",
  "expireAt": "2026-12-31T23:59:59Z",
  "userTraffic": {
    "usedTrafficBytes": 1073741824,
    "lifetimeUsedTrafficBytes": 5368709120,
    "onlineAt": "2026-02-17T14:30:00Z"
  },
  "subscriptionUrl": "https://market-plus.vip/api/sub/550e8400-e29b-41d4"
}

2. Backend API

GET /api/v2/remna/subscription

Returns the subscription URL for the authenticated user. Auto-provisions user in Remnawave on first call if not yet created.

curl -H "Authorization: Bearer $JWT" \
  https://api.shivavpn.io/api/v2/remna/subscription

Response:

{
  "subscriptionUrl": "https://market-plus.vip/api/sub/550e8400-e29b-41d4",
  "shortUuid": "550e8400-e29b-41d4",
  "active": true,
  "remainingDays": 47
}

Implementation: RemnaController.java (backend-refactoring/src/main/java/com/karmavpn/rest/)

Key logic: - Returns remnaShortUuid from account.remna_short_uuid - Calculates remainingDays from account.validTo - Returns 404 if auto-provision fails


3. Config Delivery: Two Paths

Status: feature/remnawave-config-delivery PR (not merged as of Feb 2026).

Path A: Transport Params (fast path)

Used for: XUI servers (84) + WL proxy servers (11) + Remnawave nodes with transport_params populated.

Flow:

ConfigDeliveryService.buildConfigs()
  → parse server.transport_params from MySQL
  → generate VLESS on-the-fly
  → return in /api/v2/countries response

No XUI HTTP calls or database lookups — pure in-memory construction from cached transport_params.

Path B: Subscription Fallback

Used for: Remnawave nodes without transport_params.

Flow:

if (account.remnaShortUuid != null)
  → remnaService.fetchSubscription(shortUuid)
  → parse 25 VLESS URLs
  → add to /api/v2/countries response

Code in ConfigDeliveryService.java:

// Path 1: transport_params (XUI, WL, Remnawave with transport_params)
for (var entry : byCountry.entrySet()) {
    CountryConfigResponse config = buildCountryConfig(
            entry.getKey(), entry.getValue(), account, lang);
    if (config != null) {
        countryConfigs.add(config);
        coveredCountries.add(entry.getKey());
    }
}

// Path 2: Remnawave subscription (for servers without transport_params)
if (remnaService != null && account.getRemnaShortUuid() != null) {
    List<CountryConfigResponse> remnaConfigs = buildRemnaSubscriptionConfigs(
            account, lang, coveredCountries);
    countryConfigs.addAll(remnaConfigs);
}

4. RemnaServiceImpl Methods

File: backend-refactoring/src/main/java/com/karmavpn/service/integration/impl/RemnaServiceImpl.java

Method HTTP Purpose
createUser() POST /api/users Create user (username, trafficLimitBytes, expireAt)
getUserByUsername() GET /api/users/username/{n} Fetch user by account number
getUserByUuid() GET /api/users/{uuid} Fetch user by UUID
updateUser() PATCH /api/users/{uuid} Update expireAt, trafficLimitBytes
enableUser() PATCH /api/users/{uuid}/enable Re-activate user
disableUser() PATCH /api/users/{uuid}/disable Suspend user
deleteUser() DELETE /api/users/{uuid} Remove user
listAllUsers() GET /api/users Fetch all users (for reconciliation)
fetchSubscription() GET /api/sub/{shortUuid} Get base64-encoded VLESS URLs

Configuration (application-prod.properties)

remna.enabled=true
remna.api-url=http://10.99.87.249:3000
remna.api-token=${REMNA_API_TOKEN}
remna.subscription-base-url=https://market-plus.vip/api/sub
remna.connect-timeout=5s
remna.read-timeout=15s

Usage Example

// Create user (on first claim)
RemnaUserResponse user = remnaService.createUser(
    "1234567890123456",  // account number
    0,                   // unlimited traffic
    "2026-12-31T23:59:59Z"
);
account.setRemnaUuid(user.getUuid());
account.setRemnaShortUuid(user.getShortUuid());
accountRepository.save(account);

// Get subscription URL
String url = "https://market-plus.vip/api/sub/" + user.getShortUuid();

// Update expiry on renewal
remnaService.updateUser(user.getUuid(), null, "2026-06-30T23:59:59Z");

// What the client sees
List<String> vlessUrls = remnaService.fetchSubscription(user.getShortUuid());
// ["vless://UUID@130.195.222.205:443?...", ...]

5. Implementation Phases

Phase 1: Backend Integration (COMPLETE, in production)

  • RemnaServiceImpl — 8 methods, full CRUD
  • RemnaControllerGET /api/v2/remna/subscription
  • RemnaSyncScheduler — daily reconciliation at 3:30 AM
  • DB columns: account.remna_uuid, account.remna_short_uuid (V102 migration)
  • panel_type = REMNAWAVE enum (V103 migration)
  • Properties: remna.api-url, remna.api-token

Phase 2: Telegram Bot — Shiva Light (PLANNED)

  • Python RemnaService (REST client to Remnawave API)
  • User mapping: telegram_user_id → remna_uuid, remna_short_uuid
  • Payment integration (/prolong {days})
  • Traffic display (/status)

Estimated: 2-3 days.

Phase 3: Config Delivery Merge (IN PROGRESS)

  • Branch: feature/remnawave-config-delivery (4 commits)
  • V108 migration: add transport_params to 23 Remnawave servers
  • ConfigDeliveryService: both paths (transport_params + subscription fallback)
  • NodeSyncService: on-demand user creation (ensureRemnaUser())
  • Blocker: waiting for feature/backend-refactoring merge first (145 commits, 431 tests)

Phase 4: Full XUI Migration (FUTURE)

  • Migrate 84 XUI servers to Remnawave nodes
  • Bulk user creation (400K users)
  • Decommission VPN Config Service fill_configs and cleanup_expired
  • Centralized traffic tracking via Remnawave API

Estimated: 2-4 weeks full-time.


6. Known Limitations

1. Subscription URL Leakage Risk

URL is public — anyone who knows shortUuid can fetch all VLESS links.

Current mitigation: shortUuid is 36 characters; brute-force is impractical. This is the standard risk model for subscription-based VPN.

Future: time-limited token rotation, IP-based whitelist on Remnawave panel.

2. No Bulk User Creation Endpoint

Creating 400K users one by one = 400K API calls. Known Remnawave issue: rate limit kicks in after ~20 requests before JWT expires.

Workaround: batch groups of 100 with retry logic.

Future: Remnawave /api/users/bulk endpoint, or direct PostgreSQL INSERT.

3. Single Panel Instance (SPOF)

One Remnawave panel at 10.99.87.249 manages all 25+ nodes. If down → no config updates.

Current mitigation: daily PostgreSQL backups, panel restarts quickly (Docker).

Future: Remnawave HA clustering.

4. Traffic Limits Not Enforced in Real Time

trafficLimitBytes is stored but Remnawave doesn't enforce per-user bandwidth cap in real time (XUI does via xray config). Only expireAt is enforced via scheduler.

Future: traffic limiter in Remnawave node xray config.

5. Dual UUID per User

Users with both XUI and Remnawave subscriptions have two separate UUIDs: - account.xray_uuid (XUI path) - account.remna_uuid (Remnawave path)

Future: unified ID, migrate XUI users to Remnawave UUID.


7. Client App Subscription Flow

  1. User imports URL into V2rayNG / Streisand / Hiddify Next:

    https://market-plus.vip/api/sub/550e8400-e29b-41d4
    

  2. App fetches and decodes:

    curl https://market-plus.vip/api/sub/550e8400-e29b-41d4 | base64 -d
    

  3. App parses 25 VLESS URLs, creates one connection profile per server

  4. User selects a profile and connects

  5. Periodic re-poll (typically hourly):

  6. Detects new nodes added in Remnawave
  7. Removes decommissioned nodes
  8. Updates traffic counters (if shown)

Advantages: - Zero backend dependency for config updates - Instant propagation: add node to Remnawave → clients auto-detect at next poll - Works with any VLESS-compatible client


File Purpose
RemnaServiceImpl.java Remnawave REST client (8 methods)
RemnaController.java /api/v2/remna/subscription endpoint
RemnaSyncScheduler.java Daily reconciliation (3:30 AM)
ConfigDeliveryService.java On-the-fly config (both delivery paths)
V102__remnawave_integration.sql DB migration: remna_uuid columns
V103__remnawave_panel_type.sql DB migration: panel_type enum + REMNAWAVE
V108__remnawave_transport_params.sql DB migration: transport_params for 23 nodes
planning/PLAN-XUI-TO-REMNAWAVE-MIGRATION.md Per-server migration plan (6-week timeline)
planning/PLAN-REMNAWAVE-FULL-MIGRATION.md Strategic roadmap (all 4 phases)

См. также: Ansible · Репозитории · VPN Config Service · Инвентарь серверов