ajdnikov Dashboard

AnziLock

Portal license.ajdnikov-dev.comSign-in DiscordSignatures Ed25519Keys ANZI-XXXXX-…

AnziLock is the licensing system behind the paid ajdnikov plugins and AjdnikovPanel. It was built for these plugins rather than bolted on from a marketplace: one service checks licence keys for every plugin, hands keys to buyers through Discord, runs the licence portal where you manage your keys and downloads, and carries the AjdnikovPanel dashboard to your browser. This page explains how it works from the customer's side — what happens when you buy, what your server sends, and what to do when a check fails.

What AnziLock Is

AnziLock is one self-hosted service at license.ajdnikov-dev.com with several parts that share the same accounts and licences:

PartWhat it does for you
Verification APIPlugins check their key with it on start-up and on a heartbeat. Every answer is signed, so a plugin can tell a real answer from a forged one.
Licence portalSign in with Discord to see your licences, keys, bound IPs and servers, reset IPs, claim purchases, download releases and open support tickets.
Discord bot/verify, /licenses, /claim, /reset-ips and /ticket in the ajdnikov Discord, plus automatic customer roles and key delivery by DM.
BuiltByBit linkEach purchase on BuiltByBit creates a licence waiting to be claimed. Keys are never baked into the jar.
NetworksWhere the AjdnikovPanel dashboard lives. Your hub connects out to AnziLock, so there is no dashboard port, password or tunnel on your side.
Phone appAn Android app for the portal with notifications for licences, claims, ticket replies, new versions and network status.

Bundles

One licence key can unlock several products. It verifies for each of them, grants each product's Discord roles and downloads, and shares one set of IP and server limits.

Getting Your Key

Keys look like ANZI-XXXXX-XXXXX-XXXXX-XXXXX. After you buy a plugin on BuiltByBit, BuiltByBit tells AnziLock about the purchase and an unclaimed licence is created for that plugin, tagged with your BuiltByBit username. You then claim it in one of three ways:

WhereHow
Discord ticketPress Claim a purchase on the support panel in the Discord. A private ticket opens; pick the product and enter your BuiltByBit username.
/claimIn the Discord: /claim username:<BuiltByBit name> [product].
Licence portalSign in and open Claim a purchase.

The claim is matched to the waiting licence by BuiltByBit username (case-insensitive). Staff approve it, or — when the purchase can be confirmed against BuiltByBit's purchase list — it may be approved automatically. Once approved, the key is posted in your ticket behind a spoiler, sent to you by DM, linked to your portal account, and your customer roles are granted.

If no purchase matches, staff may ask for proof of purchase before creating your licence. A rejected claim is DMed to you with the reason.

Already have a key?

Run /verify key:ANZI-… in the Discord, or use Link a key in the portal. That links an unowned key to your Discord account (creating your portal account if you never signed in) and grants your roles. A key that already belongs to someone else is refused.

Putting the Key in a Plugin

Paste the key into the plugin's config file and restart the server. The setting is named in each plugin's manual — for example licenseKey in AjdnikovPanel's config.properties. On success the console shows:

[AnziLock] License valid (lifetime) · licensed to steve
[AnziLock] Update available: 1.3.0 (you run 1.2.0)

The second line only appears when a newer version is published. A licence with an end date prints expires 2027-01-31 instead of lifetime.

On failure the plugin prints a block with the result code, a message, and the key with most of it masked, then applies the product's failure action:

[AnziLock] ================================================
[AnziLock] License check failed: IP_LIMIT_REACHED
[AnziLock] <the reason>
[AnziLock] Key: ANZI-XXXXX-…
[AnziLock] ================================================
[AnziLock] Disabling YourPlugin.

With an empty key the plugin stops with NO_KEY before contacting anyone.

How a Check Works

A plugin checks its key when it starts (verify) and then again on a heartbeat (by default every 10 minutes). Each check is one HTTPS request:

Plugin                                   AnziLock
  │  POST /api/v1/client/verify            │
  │  {key, product, hwid, nonce, …}  ───▶  │  blacklist → product → key → status → expiry
  │                                        │  → IP / server / seat limits → record usage
  │  ◀───  {payload, signature}            │  payload = {code, valid, action, heartbeat,
  │                                        │            grace, nonce, hwid, ts, …}
  │  check signature, nonce, hwid, time    │
  │  VALID   → cache the signed answer, schedule the next heartbeat
  │  invalid → run the failure action (DISABLE / SHUTDOWN / WARN)
  │  no reply → keep running on the cached answer for the grace period

AnziLock checks, in order: whether the IP or server is blacklisted, whether the product exists and is active, whether the key exists and belongs to that product (or a bundle that includes it), whether it is suspended or expired, and whether the IP, server and seat limits allow one more. The first check that fails decides the answer.

The answer is signed with AnziLock's private key. The plugin carries the matching public key, so it accepts only answers that really came from AnziLock, for this request, for this server, and no older than 15 minutes. See Security model.

On start-up the plugin tries up to 3 times with a timeout of a few seconds each, so a slow network delays start-up by seconds, not minutes.

Offline Grace & Failure Actions

When AnziLock can't be reached

Your server does not stop because the licence server has a bad minute. After every successful check the plugin stores the signed answer next to its data. If a later check cannot reach AnziLock, the plugin keeps running on that stored answer for the product's grace period (by default 1 hour) and logs how long is left:

[AnziLock] License server unreachable (…). Running on the last signed check for another 47 min.

When the grace period runs out without a successful check, the failure action runs. A server that has never verified successfully cannot run offline — the first start needs a connection.

Rate limits, replayed requests and malformed requests are not verdicts on your licence: the plugin keeps its previous state and tries again on the next heartbeat.

Failure actions

Each product chooses what happens when a licence is rejected. The choice travels inside the signed answer, so it cannot be changed on the way.

ActionWhat happens
DISABLE (default)The plugin disables itself. The rest of the server keeps running.
SHUTDOWNThe server (or proxy) shuts down.
WARNThe failure is logged and the plugin keeps working.

Limits & Resets

Every licence carries three limits. The defaults come from the product; staff can change them per licence.

LimitCountsWhen it's full
IP limitDistinct public IPs that have used the key. Unused IPs can expire on their own after a set number of hours if the product says so.IP_LIMIT_REACHED — reset your IPs
Server limitDistinct servers, identified by a hardware ID: a hash of the server folder, host name, network card addresses, operating system and user.HWID_LIMIT_REACHED — ask staff to reset servers
Online at onceServers running the key at the same time, measured by heartbeats within twice the heartbeat interval.SEAT_LIMIT_REACHED — stop one, or wait for it to time out

Resetting IPs yourself

Moved hosts, or your host changed your IP? Clear the bound IPs with /reset-ips in the Discord or Reset IPs on the licence in the portal. Self-service resets are allowed per product and have a cooldown (24 hours by default). Staff can reset IPs without a cooldown, and reset servers (hardware IDs), from a ticket.

Moving a server to a new folder or machine changes its hardware ID. If that pushes you over the server limit, open a ticket.

Result Codes

The code is printed in the console when a check fails, and listed per request on your licence in the portal.

CodeMeaningWhat to do
VALIDThe licence is good.Nothing.
NO_KEYThe config has no key.Paste your key and restart.
LICENSE_NOT_FOUNDNo such key.Copy the key again from /licenses or the portal; check for spaces.
PRODUCT_NOT_FOUND, PRODUCT_MISMATCHThe key belongs to a different plugin.Use the key bought for this plugin.
PRODUCT_INACTIVEThe product is switched off.Ask in the Discord.
LICENSE_SUSPENDEDStaff suspended the licence. You get a DM when this happens.Open a ticket.
LICENSE_EXPIREDThe licence's end date has passed. Reminders are sent by DM a few days before.Renew it, or ask in a ticket.
IP_LIMIT_REACHEDToo many different IPs used the key./reset-ips or Reset IPs in the portal.
HWID_LIMIT_REACHEDToo many different servers used the key.Open a ticket to reset servers.
SEAT_LIMIT_REACHEDToo many servers run the key right now.Stop one, or wait for it to time out.
IP_BLACKLISTED, HWID_BLACKLISTEDThis IP or server is blocked.Open a ticket.
RATE_LIMIT, REPLAY, BAD_REQUESTNot a verdict on the licence.None; the plugin tries again on the next heartbeat. If it repeats, update the plugin.
MAINTENANCEAnziLock is in maintenance.Watch the Discord.
GRACERunning on the stored answer while AnziLock is unreachable.Check the server's outgoing HTTPS connection.
GRACE_EXPIRED, NETWORK_ERRORAnziLock was unreachable for longer than the grace period, or on the very first start.Allow outgoing HTTPS to license.ajdnikov-dev.com.

The Licence Portal

Open license.ajdnikov-dev.com and choose Continue with Discord. You see only your own licences.

PageWhat's there
My licensesEach licence with its status, expiry and limits. Open one to see its bound IPs (partly masked) and servers, reset IPs, and — for AjdnikovPanel — jump to your network.
Claim a purchaseClaim a BuiltByBit purchase, as above.
DownloadsReleases of every product you hold a licence for, with changelogs.
SupportOpen a ticket and follow and answer it without leaving the portal. The conversation is the same Discord ticket channel.
NetworksYour AjdnikovPanel networks, and those shared with you. See Networks.
AccountYour BuiltByBit username (used to match claims) and phone notifications.

Roles

RoleCan
CustomerSee own licences, reset own IPs (if the product allows), claim purchases, download releases, open tickets.
StaffEverything above for every customer, plus create, edit and suspend licences, review claims, manage blacklists and read the audit log.
AdminEverything, plus products, releases, settings and the signing key.

Discord Commands

The AnziLock bot runs in the ajdnikov Discord. All replies are ephemeral: only you see them.

CommandWhat it does
/verify key:<ANZI-…>Links a key to your account and grants your roles. Refuses keys owned by someone else.
/licensesLists your licences with status, expiry and limits.
/claim username:<BuiltByBit name> [product]Opens a purchase claim. Delivered instantly when the purchase can be confirmed automatically; otherwise staff review it.
/reset-ips license:<pick>Clears the licence's bound IPs, subject to the product's rules and cooldown.
/ticketOpens a private ticket channel.

Roles

Each product can grant Discord roles. You hold a product's roles while you own at least one licence for it that is active, not expired and not suspended. Roles update the moment that changes, and are re-checked every 30 minutes.

Direct messages

AnziLock DMs you when a key is delivered (with the download link), a few days before a licence expires, when it expires, when it is suspended, and when a claim is rejected.

Tickets & Panels

Support panels in the Discord are messages with buttons. Each button opens a private ticket channel (visible to you, the bot and staff), opens an application form, starts a purchase claim, or links to a page. The Custom plugins panel asks five questions about the plugin you want and opens a ticket with your answers in it — see Custom Requests.

You can have one open ticket per kind of button, so an open support ticket does not block a plugin application. When a ticket is closed it is archived or removed from the Discord; the conversation is saved first, so staff can still read it.

Networks

The AjdnikovPanel dashboard is served by AnziLock, not by your machine. Your hub opens one outgoing WebSocket to AnziLock (the relay) and proves itself with its licence key and hardware ID. AnziLock answers with a welcome signed by the same key as licence answers, so a fake server cannot drive your hub. From then on AnziLock signs people in with Discord, decides who may open the network, and passes their requests down that socket.

  • No port, password, address or tunnel on your side — only outgoing HTTPS (port 443).
  • Every 15 seconds the hub reports players and server states for the network list. Nothing about individual players leaves the hub except while someone is looking at the dashboard.
  • You can share a network with your staff as Viewer or Admin, and give AnziLock staff read-only support access for 1 hour to 7 days. Without a grant, staff see only whether your network is up, its players, servers and version.
  • Every visit by someone other than the owner, every change, and every sharing change is written to the audit log.

Setup, sharing and the relay in detail: AjdnikovPanel manual → Licensing & Relay.

Phone App

The AnziLock Android app opens the portal and adds notifications. Turn them on inside the app under Account → Phone notifications and pick what you want to hear about:

NotificationWhenOn by default
New licensesA licence is added to your account.yes
Expiry remindersA licence is about to expire, or has expired.yes
SuspensionsOne of your licences is suspended.yes
Purchase claimsYour claim is approved or rejected.yes
Ticket repliesStaff reply to your ticket, or close it.yes
New versionsA new version of a plugin you own is out.no
Network statusOne of your networks goes offline or comes back.yes

Notifications arrive instantly by push, and the app also checks about every 15 minutes and whenever it is opened. You can pin up to 5 networks: each stays in the notification shade with its status, players and servers, and has an Unpin button.

Notifications are only stored for accounts with a phone switched on, and are deleted after 14 days. Switching the phone off in the app revokes its access; phones not heard from in 90 days are removed.

Security Model

The check itself

  • Every answer, valid or not, is signed with Ed25519. The plugin embeds the public key and rejects anything unsigned or mis-signed.
  • The plugin sends a fresh random nonce with every request. The answer must echo it, and AnziLock refuses a nonce it has seen in the last 10 minutes, so old answers cannot be replayed.
  • The answer echoes the server's hardware ID, so an answer for one server cannot be reused on another.
  • The answer carries a timestamp; the plugin rejects answers older than 15 minutes.
  • The stored offline answer is the last signed valid answer, bound to the key and hardware ID, and honoured only for the grace period.
  • The failure action is part of the signed answer, so it cannot be downgraded on the way.

The service

  • Licence keys are stored encrypted (AES-256-GCM), looked up by a keyed hash. A database dump alone does not reveal keys.
  • Portal sessions are random 256-bit tokens, stored hashed, in HttpOnly; Secure; SameSite=Lax cookies. Signing out and bans end them.
  • Every state-changing portal request needs a CSRF header and a matching origin; every input is validated; strict security headers and a Content-Security-Policy are set.
  • Rate limits per IP on every route, and 60 checks per minute per licence key.
  • Blacklists for IPs and hardware IDs are checked before any licence logic.
  • Every staff action — including revealing a key — is written to an audit log with who did it and from where.
  • AnziLock runs as a non-root container behind a Cloudflare Tunnel, with no open inbound port.

What a Plugin Sends

Each check sends exactly this, and nothing about your players or worlds:

FieldWhat it is
keyYour licence key
productWhich plugin is asking
hwidA one-way hash identifying the server (see Server limit)
nonceA random value for this request
versionThe plugin version, used for the update notice
serverName, portSo you can tell your servers apart on the licence page
playersThe online player count

AnziLock also sees the public IP the request comes from, which is what the IP limit counts. Request logs are kept for a limited number of days and then pruned. What AnziLock stores, for how long, and how to have it deleted is set out in the Privacy Policy; for questions about your data, open a ticket.

Troubleshooting

SymptomCheck
The plugin disables itself on startThe console block names the code — see Result codes.
IP_LIMIT_REACHED after moving host/reset-ips, or Reset IPs on the licence in the portal. If the cooldown is still running, open a ticket.
Could not reach the license server … no cached verificationThe first start needs outgoing HTTPS to license.ajdnikov-dev.com. Some hosts block outgoing connections by default; ask them to allow it.
Response failed verificationSomething between your server and AnziLock altered the answer: a proxy, a filtering firewall, or tampering. The plugin refuses to trust it.
My claim found no matchYour BuiltByBit username must match the one on the purchase exactly (case doesn't matter). Set it under Account, or answer staff in the ticket.
I didn't get my rolesRun /verify with your key, or check that the licence shows as active in /licenses.
My network isn't under NetworksYou are signed in with a different Discord account than the licence owner, or the hub runs a version older than 1.2.

Need more help?

Open a ticket in the Discord server or from Support in the portal.