Skip to content
SharX Connect

Subscription HTTP request

Implementation: SubscriptionFetcher in SharXCore. The request is a plain GET via URLSession.shared.data(for:) with a 60 s timeout; HTTPS is expected but not enforced (the OS handles TLS).


Request headers

Every header the client sends with a subscription GET:

Header Value Set by Always sent?
Accept */* SubscriptionFetcher.fetchDetails yes
User-Agent SharXConnect/<version>/<platform>/<build> SubscriptionFetcher.defaultUserAgent yes
x-hwid Stable device id (hex, 32 chars) SubscriptionDeviceHeaders yes
x-device-os iOS | macOS | Unknown SubscriptionDeviceHeaders yes
x-ver-os OS version (18.2, 26.0.1) SubscriptionDeviceHeaders yes
x-device-model Marketing name (iPhone 14 Pro Max, iPad Pro 11-inch (M4)); in the simulator uses SIMULATOR_MODEL_IDENTIFIER; unknown identifiers fall back to raw hw.machine SubscriptionDeviceHeaders.deviceModelName yes
X-SharX-Refresh-Planned-Seconds Seconds until the next scheduled refresh SubscriptionRefreshService.refresh only when auto-refresh is enabled on the subscription

Accept: */*

The client deliberately does not ask for text/html: many panels respond with an SPA page instead of the raw node list when they see text/html in Accept. If the body starts with <!DOCTYPE or <html, the parser rejects it with receivedMarkupInsteadOfSubscription.

User-Agent

Format: SharXConnect/<shortVersion>/<platform>/<build>.

  • <shortVersion>CFBundleShortVersionString (fallback 1.0).
  • <platform>ios | macos | tvos | watchos | apple.
  • <build>CFBundleVersion (fallback 1).

Example: SharXConnect/1.0/ios/42.

Recommendation for panels: key templates on the SharXConnect/ prefix, not the full string — version and build change with each release.

x-hwid and HWID headers

Standard panel fields for HWID-limited panels (Remnawave and compatible). The id is generated once and stored in Keychain; it survives app reinstalls and is shared across targets (main app, PacketTunnel, widgets) via the access group.

Remnawave docs: HWID device limit.

Panel response when the limit is reached. Remnawave 2.7.5+ returns HTTP 200 but sets the header x-hwid-max-devices-reached: true (and x-hwid-limit: true for v2RayTun backwards compatibility). SubscriptionFetcher detects this before inspecting the status code and throws SubscriptionFetchError.deviceLimitReached; the UI shows a localized message that asks the user to remove one of the devices in the panel profile or to ask the administrator to raise the limit. Remnawave's REST endpoint that adds a device returns error code A099 ("User hwid device limit reached", HTTP 400) — SharX does not call this endpoint directly, but the panel's logs correlate that code with the same header flags.

X-SharX-Refresh-Planned-Seconds

Tells the panel the expected interval (in seconds) until the next subscription refresh. Helps the panel plan rate limiting. The value matches entity.effectiveAutoRefreshIntervalSeconds. The header is not sent when the user has disabled auto-refresh on that subscription.


Response headers

The client reads response headers case-insensitively and collects values for the keys listed in SubscriptionBodyDirectives.knownKeys. The same set of keys is simultaneously supported as #key: value directives at the top of the body — see subscription-body-and-routing.md.

Priority: if the same key appears in both the HTTP header and a body directive, the HTTP header wins (SubscriptionRefreshService.refresh performs merged = body.merging(headers) { $1 }). Rationale: the header reflects server state more reliably than a cached / templated body.

Panel → client: UI theme (sharx-color-scheme, sharx-accent-palette)

The panel can push appearance and accent palette into the app on each successful subscription refresh:

  • As response headers (names case-insensitive): sharx-color-scheme, sharx-accent-palette.
  • Or as leading body directives: #sharx-color-scheme: … / #sharx-accent-palette: … (same rules as other #key: value lines — see subscription-body-and-routing.md).

Allowed values (lowercase; matching is case-insensitive):

  • sharx-color-scheme: automatic, light, dark.
  • sharx-accent-palette: system, aurora, midnight, sakura, sunset, graphite, matrix, crimson.

They are written to UserDefaults.standard under sharxColorSchemePreference and sharxAccentPalette (the same keys as @AppStorage in Theme.swift), so the UI updates on next render. Invalid values are ignored. If a key is absent, the client does not reset that setting (previous user or panel value stays).

For inline subscriptions (paste / share-body without http), leading #sharx-… lines are also parsed and applied.

Implementation: SubscriptionPanelThemeDirectives.applyFromMergedDirectives.


Common errors

Error When
SubscriptionFetchError.invalidURL sourceURLString doesn't parse as a URL
SubscriptionFetchError.httpFailure(code) HTTP status ≠ 2xx
SubscriptionFetchError.emptyResponseBody HTTP 200 with an empty body (empty panel template, Fallback without XRAY_BASE64, …)
SubscriptionFetchError.deviceLimitReached Remnawave set x-hwid-max-devices-reached: true or x-hwid-limit: true — HWID limit reached
SubscriptionFetchError.network(error) Transport: timeout, DNS, offline, …
SubscriptionParseError.receivedMarkupInsteadOfSubscription Body starts with HTML (<!DOCTYPE, <html)

See also

Russian version