Device Limit (HWID)
HWID is a device identifier that the client app sends along with a subscription request. The panel uses it to tell that the same device is requesting the subscription rather than a new one, and can limit the number of devices.
The feature is fully optional and off by default. It works only with clients
that can send the x-hwid header.
How to enableâ
The limit is configured in the panel under Subscription â Settings â HWID
Device Limit, not in .env. Three parameters are available: the switch
itself, the default device limit, and the notice text for the client. You can
override the settings for an individual external squad, and set a custom limit
for each user.
| Place | What it sets |
|---|---|
| Global subscription settings | enables the limit, the default limit, the notice text |
| External squad | overrides the global settings for its users |
| User card | a personal device limit for one user |
If no limit is set for a user, the default limit applies. A value of
0 in the user card disables the limit for that user: devices are still
recorded, but the subscription is always issued.
If the limit is enabled and the client does not send the x-hwid header, the
subscription is not issued. Before enabling it, make sure your users have
supporting clients, otherwise they will lose access.
Which headers the client sendsâ
| Header | Required | Description |
|---|---|---|
x-hwid | yes | device identifier |
x-device-os | no | platform, for example iOS |
x-ver-os | no | OS version |
x-device-model | no | device model |
user-agent | no | the app and its version |
The x-hwid value must be 10â64 characters: Latin letters, digits,
and the = and - signs. If the format does not fit, the panel drops the header
entirely and treats the HWID as not provided.
curl -H 'x-hwid: UE42LJXu4DbiCaBv' \
-H 'x-device-os: iOS' \
-H 'x-ver-os: 18.3' \
-H 'x-device-model: iPhone 14 Pro Max' \
https://sub.example.com/XXXXXXXXXXXX
In response, the panel adds service headers that the client uses to show the user a clear message:
| Response header | When it appears |
|---|---|
x-hwid-active | the limit is enabled on the panel side |
x-hwid-not-supported | the limit is enabled, but the client did not send x-hwid |
x-hwid-max-devices-reached | the maximum number of devices has been reached |
x-hwid-limit | the same, for compatibility with older clients |
What is storedâ
Every device goes into the hwid_user_devices table. The key is the
"HWID + user" pair, so the same device under two users counts as two records.
| Column | Contents |
|---|---|
hwid | device identifier, up to 64 characters |
user_id | subscription owner |
platform | platform from x-device-os |
os_version | OS version from x-ver-os |
device_model | model from x-device-model |
user_agent | app from user-agent |
request_ip | IP address of the subscription request |
created_at, updated_at | when the device was first and last seen |
What the panel showsâ
The user card displays the list of that user's devices: platform, OS version, model, IP, and dates. You can delete devices one by one or clear the whole list â after that, a device can connect again. There is a search by HWID.
A separate HWID page collects a summary: distribution by platform and
app, the total number of unique devices and records, the average number of
devices per user, and the list of users with the most devices. The same data is
available through the API â the GET /api/hwid/devices,
GET /api/hwid/stats, and GET /api/hwid/devices/top-users commands.
Device added and deleted events (user_hwid_devices.added and
user_hwid_devices.deleted) arrive in webhooks. See
Webhooks for details.
Retention and privacyâ
The hwid_user_devices table is the most sensitive one in the database: it holds
the device identifier, model, OS version, user-agent, and IP address. It is
not cleaned automatically until you enable auto-cleanup.
| Variable | Default | What it does |
|---|---|---|
SERVICE_CLEAN_OLD_LOGS | false | enables weekly cleanup of sensitive tables |
HWID_DEVICES_RETENTION_DAYS | 90 | how many days to keep a device |
The countdown starts from updated_at, that is, from the last time the device
appeared: actively used devices are kept, while those that have not come back for
longer than the retention period are deleted. The job runs once a week, on Monday
night.
If you do not need device history, shorten the retention period or clear the table manually â through the Logs section in the panel or the rescue CLI.
Deleting a device removes the protection against subscription sharing: the user can connect it on a new device if a free slot is available. Compare device lists before a mass cleanup.
What's nextâ
- Webhooks â device events and payload signing.
- Notifications â threshold and expiry alerts.
- Rescue CLI â mass device cleanup.
- Environment Variables â retention periods.