What is Bot Client (Detuks Client) really doing?
Bot Client is owned and operated by Detuks. However, following the shutdown of Storm Client, Storm's former owner Burak resurfaced on Bot Client (Detuks Client) under the fresh account Romandinho10 (Roman - G Plugins), where Detuks gave him the "Premium Developers" role to distribute plugins.
Both Detuks and incoming DreamBot developer Aeglen have openly admitted in Discord that Allure compromised user accounts. Furthermore, users inspecting Detuks's client logs found it running the exact same OpenOSRS codebase that Storm used, while Roman magically dropped a complex ToA script within days of joining.
Accounts Are Still Being Hijacked
Victims are reporting hijacked RuneScape accounts tied to Bot Client's cloud hosting. Detuks confirms a "quite serious" breach of cloud credentials entered on the website — then blames an employee of a "100b+ company" hosting provider. Members, rival client owners, and the wider community aren't buying it.
~30 users
Cloud accounts accessed, per Detuks
UK logins
Jagex flagging suspicious logins on victims
Plain text
Members allege logins stored without encryption
SEP 18, 2026 — THE HIJACKS SURFACE
Victim Gets Jagex "Suspicious Login" Alert From the UK

Takeaway: A victim posts an official Jagex hijack warning — and the first response from Bot Client staff is to blame the victim's timing instead of investigating.
1:20 AM — DETUKS CONFIRMS THE BREACH
"This Is Quite Serious Issue" — Cloud Credentials Accessed

Takeaway: Detuks admits cloud hosting credentials entered on the website were accessed — while pre-emptively insisting his own code has "no vulnerabilities" and pinning everything on a hosting provider employee.
THE OFFICIAL THEORY — ~30 USERS, "KEEP IT PRIVATE"
Detuks Blames a Rogue Employee at a "Big Big Company"

Takeaway: The official story asks you to believe a rogue employee at one of the world's largest companies risked their career — and asked victims to keep it quiet in the meantime.
THE COMMUNITY ISN'T BUYING IT
Two Theories — and Only One Sounds Believable


Takeaway: Even Detuks's defenders can only offer promises — while critics point at the unencrypted credential store sitting on Detuks's own server.
MEMBERS ALLEGE PLAINTEXT STORAGE
Members Say Logins Were Stored Without Encryption
🤡🤡 storing user accounts w/o encryption is just lazy 😂😂 assume it's from his "bot hosting" on bot client
They have a sketchy past doing sketchy things idk what more ppl want to validate, you think someone making multiple hundreds / thousands daily is stealing gold?
Screenshots pending — quoted verbatim from Discord

If you're storing something that can be used to log in to an account, it should be encrypted. 0 reason for it to not be.
Screenshot pending — quoted verbatim from Discord

Takeaway: Detuks claims "encrypted at rest" — while members allege logins were stored in plain text. A rival client owner publicly warns users while advertising that Kovex encrypts everything.
DAMAGE CONTROL
"Your Credentials Are Safe" — Trust Us

some cloud provider nonsense happened a few weeks ago apparently
Screenshot pending — quoted verbatim from Discord
Takeaway: After the breach of cloud credentials, the official line is "stored locally, just like the Jagex launcher" — directly contradicting the admission that website-entered cloud accounts were accessed.
Detuks Says vs. What Members Say
Detuks says
- "There are no vulnerabilities in the backend code"
- "Data is encrypted at rest by default"
- "Your account credentials are safe. They are stored locally"
- Only "a few users" impacted — a rogue hosting employee did it
Community members say
- "storing logins in plain text is pretty valid to crash out about 😂 pretty embarrassing too" — Culturalism (community member)
- "storing user accounts w/o encryption is just lazy" — Culturalism (community member)
- "detuks storing unencrypted customer credentials was accessed" — community report
- "It should be encrypted. 0 reason for it to not be." — Nezz
1. If you ever entered cloud hosting credentials on the Bot Client website, rotate every one of them immediately — Detuks confirms that database table was accessed.
2. Check your Jagex account for unrecognized logins and secure it via account.jagex.com (Forgot password flow) if anything looks off.
3. Enable the Jagex Authenticator on every account and never reuse those passwords anywhere else.
4. Do not store RuneScape logins in any third-party client or website that cannot show you encrypted storage.
Clarifying the Roles: Detuks, Burak, and Allure
A breakdown of who owns the platform, who wrote the plugins, and who is currently active.
Detuks is the sole owner and operator of the Bot Client platform.
Detuks accepted former Storm Client developers onto his platform and assigned Burak the Premium Developers role.
Burak was the owner of storm-client.com. After Storm shut down, he created the fresh account Romandinho10 (Roman - G Plugins).
He possessed Storm's plugin source code and re-released it on Detuks's client under the "G Plugins" brand.
Allure and Burak are two different people.
Allure developed the original Allure plugin suite for Storm Client. Burak, as Storm's owner, had the source code and is now republishing it.
Detuks & Aeglen Openly Admit Allure Compromised Accounts
Discord statements from Detuks (Bot Client owner) and Aeglen (DreamBot developer migrating to the platform) acknowledging Allure's credential theft history. Click any image to pop out and view full-size.

Takeaway: Detuks does not dispute credential theft occurred. He openly acknowledges Allure "went a bit too far" and attempts to normalize it by claiming regular users are "fine".

Takeaway: A Bot Client team member (jim the scatman, DTKS role) does not deny the logging — he openly confirms "you guys are logging details" and justifies Allure's credential theft as "retaliation" that "clearly it worked".

Takeaway: Aeglen explicitly confirms to his userbase that a scripter on Detuks's platform previously messed with the OSRS accounts of users.

Takeaway: Aeglen confirms that during client negotiations, Detuks was willing to "overlook mishandling cracked account details" because Allure was an early supporter.

Takeaway: Even within Aeglen's own community, users openly describe Allure as "one of the most notorious scammers in OSRS" who "stole from people" — and question why Detuks would associate with him.
Burak's Fresh "Romandinho10" Account & Plugin Line
Evidence documenting Burak joining Bot Client (Detuks Client) under a fresh account and re-releasing Storm Client's plugins.

Burak operating under the fresh handle Romandinho10 on Bot Client (Detuks Client).

Detuks assigned Burak the Premium Developers role upon joining.

As Storm's owner, Burak held the uncompiled plugin source code and immediately republished the catalog under the "G Plugins" label on Detuks's client.
The "G Plugins" Line: Recycled Storm Client Codebase
Upon receiving the Premium Developers role from Detuks, Burak began releasing plugins under the "G Plugins" label. Because Burak was the owner of Storm Client, he possessed the uncompiled source code for all of Storm's plugins and has simply repackaged them for Bot Client (Detuks Client):
• G ToA (Tombs of Amascut raid automation)
• G Scurrius (Scurrius boss automation)
• G Miner (Mining automation)
• G Thiever (Thieving & pickpocketing)
• G Trekking (Temple Trekking minigame)
• G Cooker (Bulk food cooking)
• G Agility (Rooftop agility courses)
The ToA Give-Away
Months of complex raid automation appearing just days after "Roman" joined the platform.
All of the former Storm developers moved over to Detuks's client. When "Roman" created his account 2 weeks ago, he immediately released a complex Tombs of Amascut (ToA) automation plugin.
ToA plugins require extensive room mechanics, puzzle automation, gear switching, and invocation routines. Nobody writes a flawless ToA bot from scratch in a few days — it is 100% Burak recycling his Storm Client ToA codebase.
1.2GB RAM Overhead & User Pushback
DreamBot users migrating with Aeglen are openly refusing to move to Detuks's platform — even team members admit the client is bloated.

A Bot Client team member confirms client instances consume upwards of 1.2 GB of RAM each due to unoptimized overhead.

The client's Themida anti-tamper virtualization is cited as a major source of the performance overhead.

Longtime users are openly refusing to migrate, citing childish moderator behavior and concerns about the client's origins.

The community's reaction to the migration announcement was overwhelmingly skeptical and mocking, with users questioning the rebrand from Detuks to "Bot Client".
Technical Analysis: Inside the Allure Wildy Slayer Plugin
We decompiled the protected, obfuscated plugin JAR distributed for the Detuks/Bot Client platform and audited every outbound call it makes. Below are the exact findings — with the decompiled code, class names, and recovered strings to back each one up.
The "Allure" Plugins Are Written in Detuks's Own Namespace
The obfuscated core of this Allure-branded plugin ships under the com/detuks/ package — Detuks's own namespace. This is not a third-party plugin hosted on Detuks's platform: Detuks writes the "Allure" plugins himself. The readable classes (AllureWildySlayer, GameStateDump, the WebSocket message stack) sit alongside the obfuscated engine in o/a.class (58.5 KB).
package tree — as shipped in the JAR
com/detuks/e/a/ <- obfuscated core
com/detuks/e/a/o/a.class <- engine (58.5 KB)
com/detuks/e/a/h/a.java <- RSA/AES-GCM crypto
com/detuks/e/a/H.java <- hardcoded AES key
com/detuks/.../AllureWildySlayer.class
com/detuks/.../GameStateDump.class
com/detuks/.../WebSocketMessage.class
com/detuks/.../MuleStatusMessage.class5-Minute Heartbeat to api/session/check
For the entire session the plugin calls api/session/check every 5 minutes (class u/a/b). Each check-in carries the session ID, the machine's HWID fingerprint, and live plugin/player state — so the Detuks backend knows who is running, on which machine, and what they are doing at all times.
u/a/b — decompiled (simplified)
@Schedule(period = 5, unit = MINUTES, async = true)
void heartbeat() {
POST BASE_URL + "api/session/check"
session : <session id>
hwid : <machine fingerprint>
state : <plugin / player state>
}HWID Fingerprinting — What It Captures About Your Machine
The plugin assembles a fingerprint from the identity signals of your computer: your operating system name and architecture, your Windows username, the computer's name on the network, your network adapter's MAC address, and your disk/volume identifiers. These are concatenated and hashed into a single stable ID that survives OS reinstalls of the plugin, password changes, and new RuneScape accounts.
That ID rides along with every 5-minute check-in. It means the operator doesn't just know an account is botting — they know which physical machine is doing it. A HWID ban follows your computer, not your account.
hwid assembly — decompiled (simplified)
String raw =
osName // e.g. "Windows 10"
+ osArch // e.g. "amd64"
+ user.name // your Windows username
+ COMPUTERNAME // machine name on the network
+ macAddress // network adapter MAC
+ volumeSerial; // disk / volume identifier
String hwid = sha256(raw); // stable machine ID
payload.put("hwid", hwid);
// attached to every api/session/check callDiscord Webhooks — Everything It Sends, and Where
Beyond the 5-minute heartbeat, the plugin reports out to two destinations: Discord webhooks carrying your RuneScape display name and public IP address, and the Detuks backend receiving session telemetry plus event reports — ban reports, quest completions, and bug reports — with room metadata streamed over its WebSocket channel.
Seven hardcoded webhook endpoints were recovered from the JAR in full — no decoding required once the string table was restored. They are reproduced verbatim on the right. Anyone can report these endpoints to Discord for revocation, which is exactly why they are published here.
recovered webhook URLs — verbatim from the JAR
POST discord.com/api/webhooks/1153807665571565639/R1FLdA7O7ITGDfmDzsKfytTGEM3ScbUs7nzBPQFkqZi8BISZIiv9SF0vqeyWDtIXj1od
POST discord.com/api/webhooks/1153808285527461958/KdTqaMoCgCPq_8vyM3x8fZ2AoS8LtIkv5w6uXsW9SSc7NL5fm_orRyUvA3U5ZBDrNeLk
POST discord.com/api/webhooks/1198772940993470464/CnXafDh3vmNexl3OuuK9WYU9-_vvwOzF-SJ9MiajEQ5oEpIcD-cZXODZvIbpoeRKmpxf
POST discord.com/api/webhooks/1456488921117491424/FiyHQBWqqw1rTsp_X4IcxfWTeSs1VIBmxj5pOOqvYOgUYvkh9zh9X6dszwKIK7lDV4S1
POST discord.com/api/webhooks/1524177021758996742/QhYho1fnjFFR0ty1p1Wc7udTGasxfM5kwN1S-2SEvm3LftLuqh7OfKYSc-BnFoNQjkPp
POST discord.com/api/webhooks/1526339256845467762/tNcRXWx8qhY70DXuOM_36HCRCIS3bdvK-EYn7cDbZOjA9ccRLEiB3drUxCE6Iuk3OZdU
POST discord.com/api/webhooks/1526341180651212870/ZZbTicxN_zYmCVj_muXTVIVcLSa-0I8wrDzdGCHK4Qew52bOgHCDaZwOuNx4ci-kU7rq
// payload: "<rs_name> | ip: <public ip>"
// plus ban / quest / bug event reportsThe Plugin Still Writes to a ".storm2" Folder
Session debug logs (session_start / session_end JSONL) are written to %user.home%\.storm2\debug\ on your machine (class i/c, local-only, never transmitted). A Storm-branded folder inside a Bot Client plugin is definitive proof of code lineage: this is Storm Client source code, lightly rebranded, running on Detuks's platform.
i/c — local JSONL logger (decompiled)
Path dir = Paths.get(
System.getProperty("user.home"),
".storm2", "debug"); // <- Storm-branded
// writes:
// {"event":"session_start", ...}
// {"event":"session_end", ...}
// local only — never transmittedThe Actual Keys — Hardcoded AES, RSA Handshake, XOR Strings
H.java carries a hardcoded AES key — f4+Etk7Rseg"q}~?L.2nM; — used with plain Cipher.getInstance("AES") (ECB) and Base64 wrapping to encrypt/decrypt messages. Since the key ships inside the JAR, this "encryption" is purely cosmetic: anyone can decrypt exactly what the backend can.
h/a.java implements proper hybrid encryption: a random AES-256-GCM session key encrypts the payload, then RSA/ECB/OAEPWithSHA-1AndMGF1Padding wraps that session key with an embedded X509 public key — only the operator's private key reverses this. There is also j/b.java (AES/CBC config-blob decryption, key = SHA-256(password + salt)) and a leftover dev-test sender in ai.java (AES/CFB over UDP to 192.168.1.100:12345 with the placeholder key your-32-byte-key). The XOR layer scrambles every string in the JAR — endpoints, URLs, wire formats — so none of it appears in plaintext until runtime.
recovered key material — verbatim from the sources
// H.java — hardcoded AES key (verbatim)
new SecretKeySpec(
"f4+Etk7Rseg\"q}~?L.2nM;".getBytes(), "AES")
Cipher.getInstance("AES") // = AES/ECB, Base64-wrapped
// h/a.java — hybrid RSA + AES-256-GCM
sessionKey = KeyGenerator("AES").init(256)
payload = AES/GCM/NoPadding(sessionKey, iv[12])
wrapped = RSA/ECB/OAEPWithSHA-1AndMGF1Padding(
embeddedX509PublicKey, sessionKey)
// only the operator's private key reverses this
// j/b.java — config blobs
key = SHA-256(password + salt) // AES/CBC/PKCS5
// ai.java — leftover dev/test sender
UDP 192.168.1.100:12345
key = "your-32-byte-key"| String | Location | What it is |
|---|---|---|
| api/session/check | u/a/b | The 5-minute heartbeat endpoint |
| session_start / session_end | i/c | Local-only JSONL debug log → %user.home%\.storm2\debug\, never sent |
| |session: | a/q | Seeded RNG log label (session|decision), local |
| OAUTH2 → JL auto-login | a/h | Login screen index handling — presses Enter only, no token captured |
| SecretKeySpec / SecretKey | H, ai, j/b, h/a | AES/XOR + RSA embedded keys — string/API obfuscation, not credentials |
| runtime-action-token | evidence JSON | The analysis kit's own redaction label for a config toggle — not plugin code |
To keep this investigation honest: unlike the original Storm Client Allure plugins — which captured usernames, passwords, session tokens, and Discord identities — this specific build contains no plaintext password theft and no session token capture. Every candidate was swept and accounted for:
JX()— an obfuscated RuneLite API stub method, not a token (it is the widgetfontIdgetter:f2.JX(), called atGameStateDump.java:107)jx_/jX_— obfuscated method names inside the API ABI-stubs JAR (net/runelite/a/f/h.class)JxPm7/jX— coincidental characters inside XOR-encoded string blobs; the decoded constants are the knownMULE_STATUS/TRADE_REQUEST/ login-log stringsOAUTH2 → JL auto-login(classa/h) — login-screen index handling that presses Enter only; no token is ever capturedruntime-action-token— the analysis kit's own redaction label for a config toggle, not plugin code
What it does contain is constant heartbeat telemetry, HWID fingerprinting, Discord webhooks carrying your RS name and public IP, real-time mule/trade WebSocket coordination, and a .storm2 debug folder that betrays its Storm Client origins.
The absence of credential theft in one plugin does not clear the platform: the owners themselves admitted on record what Allure did previously, and the same team operates this codebase. Treat every binary from this platform as untrusted.
The Account Builder Ships Your Character, Your Discord ID and Your Machine ID to a Third-Party API
The Wildy Slayer plugin was not the only thing reporting home. A second Allure-branded JAR — the account builder — was built for exactly one purpose: upload a profile of the character you are training and read the vendor's verdict. We broke its two-stage stringer, recovered every request key by hand, and rebuilt the body down to the last byte. The full annotated decompile is below, and the wire format is reproduced exactly.
Two Endpoints on a Host That Is Not the Bot Client Backend
Everything above phoned home to the Detuks backend. This plugin does not. Both calls go to api.allureplugins.com — a separate vendor host, under a separate TLS certificate, that the Detuks platform has no stated reason to need. The reporter class is com/detuks/e/a/r; the character update worker is Wi(r$a) (1024 B of code) and the restriction worker is Wm(r$a, int, String, long) (917 B).
Both are fired by a single-threaded scheduler named Account-Builder-Character-Reporter that the plugin registers at load time. The endpoint strings are not hidden behind the XOR layer — they are static fields in the class initializer, one per URL.
r — static endpoints (recovered verbatim)
// com/detuks/e/a/r — static fields, <clinit>
a = "https://api.allureplugins.com/api/account-builder/character"
b = "https://api.allureplugins.com/api/account-builder/ban-report"
c = "text/plain; charset=utf-8"
d = Gson
e = OkHttpClient
f = ~/.detuks/allure_account_builder/telemetry
k = scheduler("Account-Builder-Character-Reporter")
l = <plugin data supplier> // character snapshot
m = <plugin data supplier> // run evidence json
o = <current session r$b>Nine Fields Bind Your Discord Account to Your OSRS Character and Your Machine
The /character body is a flat JSON object. It does not carry a password or a session token — and that is the point. It carries enough to build a durable profile keyed on a machine fingerprint, annotated with the Discord ID of the person running it, the character being trained, and how far that character has come.
The snapshot is validated before it is sent: the character name is normalized and must be non-blank, the Discord ID must be non-blank, and the session UUID is compared against the value embedded in the snapshot. A final putAll merges a plugin-supplied map into the body, so the field list is a floor, not a ceiling — the vendor can extend it without shipping a new release.
Wi(r$a) — request body, reassembled from the string table
POST /api/account-builder/character
{
"client_run_id": "<uuid minted per plugin run>",
"character_name": "<rs name, normalized>",
"discord_id": "<discord id of the operator>",
"fingerprint": "<64 hex chars>",
"plugin_version": "<version constant>",
"current_total_level": 126,
"logged_in_runtime_ms": 1234567,
"early_account_behavior_enabled": false,
"runEvidence": "<base64 status json>"
// + every key of the plugin-supplied map
}| JSON key | Source | What it is |
|---|---|---|
| client_run_id | session.wb | A fresh UUID.randomUUID() per plugin run — correlates every upload from one launch |
| character_name | r$c.wf | The OSRS character, run through normalizeCharacterName; must be non-blank |
| discord_id | r$c.wg | The Discord account tied to the licence holder — must be non-blank |
| fingerprint | Wk() | SHA-256 hex of com/detuks/e/b/t/s.Wa(), cached for the session. Ties every run to one machine |
| plugin_version | PLUGIN_VERSION | Compile-time constant — lets the vendor bucket telemetry by build |
| current_total_level | r$c.wi | Total level of the character being trained |
| logged_in_runtime_ms | field | How long the client has been logged in — an uptime beacon |
| early_account_behavior_enabled | r$c.wj | Reads the plugin's behaviour toggle. The vendor learns who switched it off |
| runEvidence | r$a.wa | Base64 of the builder-status JSON — the bulk of the body |
| runEvidenceAccepted / runEvidenceError | response | The only two fields read back — each parsed from two duplicated string slots |
runEvidence Is a Full Activity Log of the Character You Are Building
runEvidence is not an opaque blob — it is the plugin's own builder-status JSON, base64'd and handed to the same encryptor as everything else. Its keys, in source order, name every stage of the automation loop: what the account builder is currently doing, what it did last, and what progress it has made.
The captured body sizes prove the split: a body without evidence measures 354 B; the same body with evidence attached measures 1640 and 1661 B. The evidence is roughly four fifths of the payload — the identifying fields are the garnish.
AllureAccountBuilder.Zd(v, String) → String — the plaintext behind runEvidence
profileId
runId
clientType // always "detuks"
pluginVersion
builderState // error | complete
// stopped | paused | building
loggedIn
playerName
currentTask
waitReason
currentStatus
lastAction
loggedInRuntimeMs
totalLevel
questPoints
levelsGained
questsCompleted
levels
sentAtThe Second Endpoint Reports Login Restrictions — With a Server-Issued Nonce
When the plugin hits a login restriction it posts a second body to /api/account-builder/ban-report, carrying the same identity fields plus the detection time, the login index, the last login timestamp, the restriction reason and a nonce taken from SecureRandom. The vendor answers with success, recorded, the nonce echoed back and the server's own lastLoggedInAt — a request/response pair the operator can verify.
One detail leaks in the code: the public entry point Wl(r$c, int, String, long) always calls the character reporter first, which is why three /character POSTs land back-to-back at every startup. Restriction reporting is not a side channel — it is built on top of the profile upload.
Wm(r$a, int, String, long)
String nonce = Wj(); // SecureRandom hex
POST /api/account-builder/ban-report
{
"client_run_id": <session uuid>,
"character_name": <rs name>,
"discord_id": <discord id>,
"detected_at": <timestamp>,
"login_index": <int>,
"last_logged_in_at": <timestamp>,
"ban_reason": <string>,
"plugin_version": <version>,
"fingerprint": <64 hex>
}
// response
lastLoggedInAt | nonce | success | recordedRSA-1024 Key Wrap Around a Fresh AES-256-GCM Body
Both endpoints encrypt before sending, using com/detuks/e/b/g/a.V_(String data, String pubKeyB64). A new AES-256 key is generated per request, the body is sealed under AES-GCM with a 128-bit tag, the AES key is wrapped with an RSA-1024 public key, and the three blobs are length-prefixed and base64'd. Unlike the hardcoded AES key found in the Wildy Slayer build, this scheme is sound — only the vendor's private key reverses it.
Sound encryption changes nothing about what is being reported. The key protects the contents from everyone else; it does not make the contents any less a Discord ID, a character name and a machine ID. The public key ships in the jar and is reproduced verbatim on the right — anyone can verify that the envelope is real.
envelope — g.a.V_
aes = KeyGenerator("AES").init(256).generateKey()
iv = random(12)
rsa = ENCRYPT(pubKey).doFinal(aes)
ct = AES/GCM/NoPadding(aes, GCMParameterSpec(128, iv))
.doFinal(data)
putInt(rsa.length) // 128, big endian
put(rsa); put(iv); put(ct)
return base64(out)
// content-type: text/plain; charset=utf-8
// body = b64( u32be(128) || wrapped || iv || ct || tag )RSA-1024 SPKI — verbatim from the jar
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3nv5/Oe2zhA9AyCsuUS1q
/oIo0DYYU07LpLZ4uYvm4S0XofyCIR4K5qzdhGhepSrgfTL3aGFlgXThoyeo
broPOFZNKDWhZ1jJQGSc9jdQfQaO0W0axDdoTFcigRGsvI2bigZ753dq7b
Q4RPItKsip2oKaFaLjc7pN9uFpwDeSLwIDAQAB"Early Account Behavior" Is Disabled by Writing a Marker File — Nothing More
The plugin exposes a toggle for "early account behavior." Turning it off Wf(String, Boolean) creates ~/.detuks/allure_account_builder/telemetry, writes false\n to a .tmp file, and moves it to early-account-behavior-off. That is the whole mechanism. There is no call to the vendor, no server-side acknowledgement, and no evidence the flag is ever read back.
Meanwhile the value of the toggle rides along in early_account_behavior_enabled on every single upload. The vendor is told who has opted out at the moment they do — which is precisely the list you would not want broadcasting itself. The switch disables a behaviour; it does not stop the reporting.
Wf(String, Boolean) — the opt-out path
dir = user.home + "/.detuks"
+ "/allure_account_builder" + "/telemetry"
mark = dir / "early-account-behavior-off"
write("early-account-behavior-off.tmp", "false\n")
move( -> ".off" )
// no HTTP call. the file IS the opt-out.
// and early_account_behavior_enabled
// still ships in every /character body.The Stringer Is Broken — Anyone Can Repeat This
This plugin hid its strings behind the same two-stage scheme as every other class in the suite: a <clinit> builds a char table from XOR-scrambled blobs, and a(int, int) decodes one entry through a 255-case key table with a chained 8-bit rotate. Both stages are reimplemented natively — no template class, no reflection, no emulation.
Because the keystream is chained, a wrong key corrupts every character — but the key is fixed by the entry's own first byte, leaving a single unknown per string. That is why the whole table sweeps in seconds. The reporter rebuilds to exactly its declared 63 entries; the account builder to 401.
per-class constants, read out of the class being decoded
class blobs initLen mask entries
com.detuks.e.a.r 2104/25 65/11 48/95 63
AllureAccountBuilder 10081/47 13/33 56/91 401
nat.detuks.client.AppKt 2434/70 15/22 29/111 117
// stage 1
table[i][j] = blob[i][j] ^ keys[j % 7] ^ mask
// stage 2 — a(x, y)
idx = (x ^ SCRAMBLER) & 0xFFFF
K = KTABLE[c[0] & 255]
k1 = ((y & 255) - K) & 255 // even chars
k2 = (((y >>> 8)) - K) & 255 // odd chars
c[i] ^= k; k = ror3(k) ^ (c[i] & 255)
How to read this: the left column is the decompiled bytecode, the green column is the decoded plaintext for each string-table index, and the bracketed offsets are positions in the reassembled string table. The two response fields on each endpoint appear twice because the class parses each one from two duplicated slots.
To be precise, because precision is what makes the rest of this page credible: this plugin sends no password and no Jagex session token. Nobody should read the payload above as a credential stealer. What it is, instead, is an identity graph — one row per run that says this machine, this Discord account, this character, this much progress, this long a session, uploaded to a vendor host the platform never explains.
Three things make that worth acting on. It goes to api.allureplugins.com, not to the Detuks backend you think you are talking to. The opt-out is a local file, not a server-side flag, and it keeps reporting itself. And the field list is extensible: a putAll of a plugin-supplied map means the vendor can append whatever it likes — including credentials — without shipping a new build.
Combined with the admissions further up this page, the picture is a platform where the same group has already mishandled user credentials, ships plugins that profile your machine, and now runs an account builder that quietly reports a linked OSRS-and-Discord identity to a third-party endpoint. The only response that works is to not load the JARs.
The Account Builder Also Screenshots Itself Into a Vendor Discord Channel
The /character endpoint is only half the picture. The same plugin carries a second sender — com/detuks/e/b/S — that posts to Discord. It is not an account-builder class: it is the shared alert sender for the whole Detuks plugin pack, and it is capable of uploading screenshots and arbitrary files. Six vendor-owned webhook URLs are hardcoded in it. These are different channels from the seven published above — zero overlap in webhook IDs — recovered from a separate class in a separate JAR.
One Alert Client, Six Webhooks, Whole-Pack Scope
com/detuks/e/b/S posts alerts over a single-threaded ExecutorService so the plugin's main loop never blocks on a network call. It holds a 38-entry stringer table and an interned cache, and resolves the destination per client through Wh(c/d/a/T) → S$a before every send.
The method surface is the important part. Three entry points take a bare string. Three more take a BufferedImage and attach it as a PNG. One takes a Path as well — that overload will upload arbitrary files off your disk, and the recovered string table contains a ZIP content type for bundle uploads. The whole thing funnels into Wg(...).
com/detuks/e/b/S — recovered surface
// fields
a ExecutorService // single pool, alerts post async
b String[] // stringer table, 38 entries
c String[] // interned cache
// text only
V_(String) We(String)
Wd(String) // second entry point
// text + screenshot
Wa(String, BufferedImage)
Wb(String, BufferedImage)
// text + screenshot + FILE
Wc(String, BufferedImage, Path)
// internals
Wg(String,String,BufferedImage,Path) // core builder
Wf(BufferedImage) -> byte[] // PNG encode
Wh(c/d/a/T) -> S$a // pick webhook
Wj() -> String // webhook url
Wk(String,String) // multipart upload
send_discord_alert(...) // async
send_discord_alert_basic(...) // async, text only
deliverAlert(...) // builds request, postsSix Webhooks, Identified by Length
No string scan was needed. In the class's 38-entry table, exactly six entries are 121 characters long — that is the exact length of https://discord.com/api/webhooks/ plus a 19-digit channel ID plus a 68-character token. The length is the tell.
Two were recovered whole from a memory scan while the plugin was live; three more surfaced during runtime string harvesting. A webhook token is a post-only credential — anyone holding one can post into that channel as the bot, and nothing else. Which is precisely why they are published: a leaked webhook is reportable, and a channel the vendor does not want reported is a channel worth reporting.
recovered from com/detuks/e/b/S — distinct from the seven above
// six entries measured at 121 chars each
https://discord.com/api/webhooks/1414599453310062733/n6HRgsce...
https://discord.com/api/webhooks/1510741027919757457/xXZiI05i...
// recovered in a second runtime harvest
https://discord.com/api/webhooks/1549232584180109323/ES26cEDDe6...
https://discord.com/api/webhooks/1461918389051326546/iRfJX2Wm1f...
// shape: /api/webhooks/<19 digits>/<68 chars>
// tokens are post-only for those channelsThe Account Builder Calls It in Exactly Two Places
The account builder fires alerts from two methods. YJ() emits lifecycle notices — "] Plugin started." at offset 465 and "] Plugin stopped." / "Plugin stopped by user." at offset 596. Every message is wrapped in a "]…][…" bracket format with the values prefixed inside it, which is what turns a bare notice into a searchable, parseable activity log.
_z() — the same method that triggers the /ban-report POST — also pushes a restriction notice, built from the string "login_restriction" and the sentence "Restriction evidence has no verified character attribution; …". So one ban is reported twice: once as structured JSON to the vendor API, once as a chat message with an attached image.
call sites in AllureAccountBuilder
AllureAccountBuilder.YJ() // lifecycle
@465 S.Wd(text) // "] Plugin started."
@596 S.Wd(text) // "] Plugin stopped."
// "Plugin stopped by user."
text = "][" + ... + "]["
AllureAccountBuilder._z() // login restriction
@267 S.V_(text)
built from "login_restriction"
and "Restriction evidence has no
verified character attribution; ..."A Standard Multipart Upload, Built by Hand
There is no Discord SDK here. The request is assembled field by field: a payload_json multipart part carries the content object, the image goes up as files[0] with a filename, and the message body references it as attachment://<id>. Screenshots go out as image/png; arbitrary files use a generic application/… type, with ZIP reserved for bundle uploads.
Because the client encodes the screenshot in-process via Wf(BufferedImage) → byte[] and hands the bytes straight to the multipart writer, whatever the plugin had on screen at the moment of the alert is what leaves your machine. There is no user prompt and no consent step on this path.
recovered multipart body parts
{"content": // text alert body
"}
payload_json= // multipart: the json
files[0] // multipart: file field
filename= // uploaded file name
attachment:// // ref to uploaded file
image/png // screenshot content type
application/... // file content type
ZIP // bundle uploads
id // attachment id in payload_jsonOne thing to get right, because the two paths are easy to conflate: GameStateDump does carry a discordId field — but that is the linked account ID on the plugin, and it travels to api.allureplugins.com as the discord_id key on the /character body. It is not posted into the Discord channels.
What goes to Discord is the alert traffic: lifecycle notices, restriction notices, and any screenshot or file the shared sender was handed. The separation matters because it splits the exposure in two — an identity record at one vendor, and live operational visibility at another, both reachable from the same plugin with no setting to disable either.

Read it with the payload section above: the /character body is the machine-readable record, and this class is the human-readable one. The account builder keeps both fed from the same events — a startup, a shutdown and a ban each produce a Discord message, and the ban produces structured JSON as well.
1. Detuks and Aeglen have both confirmed that scripters on this platform previously compromised user credentials.
2. Do not input your primary RuneScape credentials or session tokens into Bot Client (Detuks Client) software.
3. If you have already authenticated or used Bot Client (Detuks Client) tools, change your password and reset your Jagex session tokens.
4. Enable 2FA (Jagex Authenticator) and stick to verified open-source clients like official RuneLite.
Curious about TwiLite?
Check out our report on twilite.lol →
More Evidence & Analysis Coming Soon
Additional reverse-engineering analysis, packet captures, and source diffs will be published here as the investigation progresses.