ℹ️ How this page works

Devices — provision boxes and point them at your reseller sources.

  • Xtream reseller account: Assign a device → source Xtream → paste the line's server URL + username + password. Credentials are AES-GCM encrypted on save and never shown again (only “has creds”).
  • m3u reseller account: Assign → source M3U → paste the m3u/m3u8 URL (optionally an EPG/XMLTV URL). Use Source preflight to auto-detect type + reachability before assigning.
  • Many at once: Import sources takes a CSV to attach playlists across many devices from a reseller export — m3u (device_id, source_url, …) or xtream (device_id, server, username, password, …; creds encrypted on import).
  • Add a fallback: give a device a second source (different reseller) via Playlists — lower priority. If the primary dies, health-aware auto-failover promotes the healthy fallback automatically.
  • Tag by reseller / segment (e.g. reseller-a, pilot): tags drive bulk actions and Rollouts cohorts.
  • Expiry gates a box automatically when a subscription lapses; renewing reactivates it on the next poll.
  • Config sync (the chip beside a device's status) reports whether the box is running the config the panel expects: ✓ synced, ⚠ stale (it re-pulls on the next poll), or ? no baseline. No chip is the normal state today — it means the box has never reported a config hash, because no shipped app build sends one yet. That is not a fault and needs no action.
  • Restore a box that reinstalled or switched build (it reappears under a new device id): on the new device click Restore and enter the old device id — its playlists are copied over (creds re-encrypted), non-destructively.

Reading a device row

  • pending usually is not a fault. It means the box has not fetched this playlist yet — not that anything failed. A box whose app never calls the provisioning endpoints stays pending forever, and that is correct: on the bedroom Onn box the running app does not call them at all. Read it as “not fetched yet”, and look for a real fault only when a box you know is polling stays pending.
  • Playlist chips list every playlist on the device, not just the primary. Click one — or the Playlists button — to open the full list with its priorities and filters.
  • The second line of Last seen is the source the box last reported activating. It is deliberately not “now playing”: nothing ever clears that timestamp and there is no stop signal.
    • last active: … · <time> — the box authenticated to that source at least once, at that time.
    • config fetched: … · playback never reported — the box pulled a config but has never told the panel it activated a source. The name is the playlist whose config was fetched most recently — not what is playing. A box can be streaming a portal happily while this line still says "never": the panel is only told what is playing if the app reports it, and the builds on this fleet do not. Read it as "config delivered, playback unconfirmed", never as "this one is on".
    • awaiting report — editing a playlist clears its timestamps while the counters survive, so the box has prior activity but has not reported since the edit. It refreshes on the next poll. Not a fault. The clear is per playlist, so on a multi-playlist device the surviving timestamps may not cover every playlist the box has used — the panel shows this rather than naming one it cannot rank.
  • The Filter pill is clickable. The pill alone only says how many categories are hidden, which is not enough to audit a filter — clicking it expands the actual names beneath the row, split into the locked set and the user-overridable set.

Content filter (curation)

  • What it does. The panel reads the provider's live, VOD and series category lists, decides which to hide, and stores the result as a list of names. The box matches that one list against all three content types.
  • Filter rules (under the preset) are five independent switches: which languages to keep, whether to require a wanted region, whether to hide other languages and non-Latin names, whether to hide 24/7 channels, and whether to keep all sport or only general sport plus DAZN and motorsport. A language is either wanted or not, and that one choice decides both its region tag and its language tag — so enabling French both stops FR hiding a category and starts it accepting one.
  • Two hide tiers, and the difference matters. Hidden (hide_categories) is a baseline the viewer can un-hide on the box with no PIN — curation, not safety. Lock (forced_hide) is never listed and every re-show path demands the unlock PIN.
  • Block adult content is a separate opt-in from the region preset, because a US adult category is US and the region rules alone will keep it. Adult categories always go to the locked tier — in the un-PINned tier they would be one tap from visible. A locked set with no PIN is refused: nobody could ever un-hide it on the box.
  • Block news is the same kind of control, and it is the only way to express “no news”: none of the region, language, 24/7 or sports rules matches news, so under the default English-only filter US| NEWS is a wanted region and is kept. Matched by pattern and always PIN-locked, like adult. The two are independent — a device can keep adult content and still drop news, or the reverse.
  • Finding things in a long list. A provider can have well over a thousand categories. Use the search box to narrow the list, then Keep all shown / Hide all shown to act on exactly what is showing. Categories caught by a pattern gate are skipped by a bulk action while “Block adult content” or “Block news” is on, and the count skipped is reported along with which gate did it. A locked row says which pattern caught it (🔒 adult, 🔒 news, 🔒 adult+news), and Show → News only lists exactly what the news pattern matches.
  • New categories fail OPEN, on purpose. The saved filter is a snapshot of names, so a category the provider adds later is in neither list and is visible on the box. That is the right default — a filter should not blind-hide what nobody has reviewed — but it means a filter drifts. Categories added since the last save are marked NEW after a preview, and Check for new on a playlist row reports the count without opening the editor. Adult and news are the exceptions: both are matched by pattern, so an unseen adult or news category is blocked without having been reviewed.
  • The list you see when editing is the HIDDEN set, not the provider's catalogue — a profile records what it hides and never what it keeps. Click Preview categories to load the provider's full list.
  • Some providers refuse the panel but answer the box. If a preview says the panel could not read a provider, that says nothing about whether the box can use it — one account on the fleet resets the connection to Cloudflare while the TV boxes reach it fine. You can still edit the hidden list by hand and save.

What actually reaches a box, and when

  • A filter only reaches a device through /api/provision/poll or /api/profile/:id. A device whose app does not call those will never apply a filter, no matter what this page shows. The panel cannot tell you that has happened — it can only show what it stored.
  • Adding a playlist sets a device to assigned. Editing one does not, and a fresh provisioning start puts it back to pending — so after editing, use Re-arm and let the box poll, or the poll returns pending indefinitely with nothing saying why.
  • Editing a playlist replaces it. The form sends the whole record; do not build one by hand from a partial payload, or the credentials are re-encrypted empty and the playlist becomes silently unusable.

Longer walkthroughs — the assign flow end to end, curating a provider, and telling “not fetched yet” apart from “broken” — are in docs/OPERATOR_GUIDE.md in the repository.

Assign a device

Enter the code shown on the device (or pick a Pending row below), then assign a portal + Xtream login.

Devices

● DeviceOwnerStatusIPCountryISP AppLast seenFilterActions
Loading…

Live Connect codes

CodeStatusDeviceExpires
—

Provisioning log

TimeActorDeviceEventIPCountryDetail
—