Try it before you use it
This script opens the Key System with a single valid key, COOKIES-DEMO (case-insensitive). Paste it into your executor and try a wrong key and a right one:
local KeySystem = loadstring(game:HttpGet("https://raw.githubusercontent.com/Lilwero20/CookiesLib/main/Cookieskeysystem.lua"))()
local ok, key = KeySystem.Prompt({
Title = "Test",
Keys = { "COOKIES-DEMO" },
CaseSensitive = false,
SaveKey = false,
})
print("Key valid:", ok, key)It's also inside the CookiesLib demo menu: open the Personalizar (Customize) tab and press Abrir key system. There the key is also COOKIES-DEMO, and 3 mistakes trigger a 10-second wait.
loadstring(game:HttpGet("https://raw.githubusercontent.com/Lilwero20/CookiesLib/main/CookiesDemo.lua"))()Quick start
Load the library and the Key System, ask for the key and, only if it's valid, create your window. If you pass Library, the Key System uses its theme (including the one the player picked) and its fonts.
local Library = loadstring(game:HttpGet("https://raw.githubusercontent.com/Lilwero20/CookiesLib/main/CookiesLib.lua"))()
local KeySystem = loadstring(game:HttpGet("https://raw.githubusercontent.com/Lilwero20/CookiesLib/main/Cookieskeysystem.lua"))()
local ok, key = KeySystem.Prompt({
Library = Library,
Title = "My Hub",
Subtitle = "Key System",
KeyLink = "https://example.com/get-key",
DiscordLink = "https://discord.gg/xxxxxx",
Keys = { "COOKIE-1234", "COOKIE-5678" },
})
if not ok then return end
local Window = Library:CreateWindow({ Title = "My Hub" })KeySystem.Prompt(options) waits until the player validates or closes, and returns ok, key. It's a shortcut for KeySystem.new(options):Wait().
How the key is checked
The first available method is used, in this order:
- 1`KeyForge`: external key service (see below).
- 2`Validate`: your own function.
- 3`KeyUrl`: a URL with one key per line (cached for 30 seconds).
- 4`Keys`: a list inside the script.
Validate = function(key)
if key == "secret" then
return true, "Welcome"
end
return false, "Wrong key"
end,
KeyUrl = "https://raw.githubusercontent.com/user/repo/main/keys.txt",Validate returns whether the key is valid and, optionally, a message for the player. Spaces at the start and end of the key are ignored, and with CaseSensitive = false upper or lower case doesn't matter.
A Keys list inside the script can be read by anyone who opens the script. For anything serious use KeyUrl, Validate against your server, or KeyForge.
Customizing it
You can change the theme, the color, the texts and the help steps. This example gives it a pink theme and custom texts:
KeySystem.new({
Library = Library,
Theme = "Strawberry",
Accent = Color3.fromRGB(255, 105, 140),
Title = "My Hub",
Subtitle = "Access",
Description = "Enter your key to unlock the hub.",
Steps = {
"Press Get key to copy the link",
"Complete the steps in your browser",
"Paste your key and press Verify",
},
Keys = { "COOKIE-1234" },
CaseSensitive = false,
MaxAttempts = 3,
Cooldown = 10,
Blur = true,
})| Option | Default | What it does |
|---|---|---|
Library | nil | Inherits CookiesLib's theme and fonts |
Theme | nil | Name of a CookiesLib theme or a color table |
Accent | nil | Accent color for the Key System only |
Title / Subtitle | "Cookies Hub" / "Key System" | Header texts |
Logo | Cookies logo | Image; false removes it |
Description | English text | Text under the title |
Steps | 3 English steps | Up to 3 steps in the “Need a key?” panel |
Note | nil | Free text instead of the steps |
KeyLink | nil | Link for the Get key button |
DiscordLink | nil | Join Discord button |
CaseSensitive | true | false ignores upper and lower case |
SaveKey | true | Allows remembering the key on the device |
AutoVerify | true | Checks the saved key automatically on open |
MaxAttempts | 5 | Failed attempts before the lock |
Cooldown | 15 | Lock duration in seconds |
Blur / Shadow | true | Background blur and window shadow |
Folder / FileName | "CookiesHub" / auto | Where the key is saved |
Id | automatic | Keeps each script's key and lock separate |
Buttons only appear when they have something to do: Get key with KeyLink or KeyForge, Join Discord with DiscordLink, and Paste if the executor has clipboard access.
Object and events
KeySystem.new returns an object you can control from your script:
local ks = KeySystem.new({ Keys = { "COOKIE-1234" } })
local ok, key = ks:Wait()
ks:Submit("COOKIE-1234")
ks:SetStatus("Checking...", "Warning")
ks:Close()
ks:Destroy()| Method | What it does |
|---|---|
ks:Wait() | Waits until it finishes and returns success, key |
ks:Submit(key) | Submits a key from code (no argument uses what's typed) |
ks:SetStatus(text, type) | Message under the button: Success, Warning, Error or nil |
ks:Close() | Closes without validating |
ks:Destroy() | Closes and cleans everything up |
Instead of waiting with Wait() you can use events:
KeySystem.new({
Keys = { "COOKIE-1234" },
OnSuccess = function(key)
loadstring(game:HttpGet("https://example.com/my-hub.lua"))()
end,
OnFail = function(key, message)
warn("Wrong key:", key, message)
end,
OnClose = function()
warn("Closed without a key")
end,
})How it behaves
- Remembered key: if the executor can save files, a *Remember key* checkbox appears. A valid key is saved and, next time, it's filled in and checked automatically. If it stops being valid, it's deleted.
- Attempts: the remaining attempts are shown. When they run out, the button locks for
Cooldownseconds, even if the script is re-run in the same game. - Connection errors (URL down, KeyForge not responding, an error in
Validate) don't use up attempts. - The window can be dragged from the top and adapts to small screens.
Several scripts with keys
Each Key System has its own Id, so several scripts can use it at the same time without clashing: each saves its key in Folder/keys/<Id>.txt and has its own attempt lock.
| If you use | Automatic `Id` |
|---|---|
Id = "my-hub" | my-hub |
KeyForge | kf_<scriptId> |
KeyUrl | url_<URL hash> |
Keys | keys_<list hash> |
Only Validate | The Title |
If two of your scripts use Validate with the same Title, give them different Ids:
KeySystem.new({ Id = "blox-fruits", Title = "Cookies Hub", Validate = check })
KeySystem.new({ Id = "steal-an-egg", Title = "Cookies Hub", Validate = check })KeyForge
Optional integration with KeyForge, the service Cookies Hub uses. KeyForge handles key status, limits, HWID and script delivery. Its SDK downloads in the background when the window opens, so checking is almost instant.
KeySystem.new({
Library = Library,
DiscordLink = "https://discord.gg/xxxxxx",
KeyForge = {
projectId = "YOUR_PROJECT_ID",
scriptId = "YOUR_SCRIPT_ID",
integrationToken = "YOUR_TOKEN",
},
LoadScript = true,
})The IDs and the token come from the KeyForge dashboard: edit a keyed script, open In-game UI and choose Custom Lua GUI. Official KeyForge guide.
| Button / moment | Call | What the window does |
|---|---|---|
| Get key | client:getKeyUrl() | Copies the key page link |
| Verify | client:verify(key) | Continues if valid; otherwise shows KeyForge's message |
After validating (LoadScript = true) | client:loadScript() | Loads the protected script. If it fails, the window stays open and shows why |
| Code | Meaning | Uses an attempt? |
|---|---|---|
KEY_INVALID | The key doesn't exist or is empty | Yes |
KEY_EXPIRED | The key expired | Yes (and the saved one is deleted) |
KEY_REVOKED | The key was revoked | Yes (and the saved one is deleted) |
KEY_SCRIPT_MISMATCH | The key belongs to another script | Yes |
HWID_MISMATCH | The key is tied to another device | Yes |
RATE_LIMITED | Too many attempts on KeyForge | No |
SESSION_EXPIRED | The verification expired | No |
NETWORK_ERROR | Couldn't connect to KeyForge | No |
PROJECT_DISABLED | Project disabled or paused | No |
Don't put secrets or validation rules inside your script: a dumped script can be read. Leave that to KeyForge or your server.
Full example
A Key System with the CookiesLib theme, 3 attempts and a 10-second wait, and a window that only appears with a valid key. The test key is COOKIES-DEMO:
local Library = loadstring(game:HttpGet("https://raw.githubusercontent.com/Lilwero20/CookiesLib/main/CookiesLib.lua"))()
local KeySystem = loadstring(game:HttpGet("https://raw.githubusercontent.com/Lilwero20/CookiesLib/main/Cookieskeysystem.lua"))()
local ok, key = KeySystem.Prompt({
Library = Library,
Title = "My Hub",
Subtitle = "Key System",
KeyLink = "https://example.com/get-key",
DiscordLink = "https://discord.gg/xxxxxx",
Keys = { "COOKIES-DEMO" },
CaseSensitive = false,
SaveKey = false,
MaxAttempts = 3,
Cooldown = 10,
OnFail = function(_, message)
warn(message)
end,
})
if not ok then return end
local Window = Library:CreateWindow({ Title = "My Hub", Subtitle = "Unlocked" })
local Main = Window:CreateTab("Main", "M")
local Welcome = Main:CreateSection({ Name = "Welcome", Full = true })
Welcome:AddAlert({ Type = "Success", Title = "Key accepted", Content = "Your key: <b>" .. key .. "</b>" })
Welcome:AddButton({ Name = "Say hi", Primary = true, Callback = function()
Window:Notify({ Title = "My Hub", Content = "Hi!", Type = "Success" })
end })
Window:CreateSettingsTab()Common problems
- It doesn't remember the key: the executor must support
writefile,readfile,isfolder,makefolderandisfile, and *Remember key* must be checked when validating. - The Get key button doesn't show up: it needs
KeyLinkorKeyForge. - I got locked and re-ran the script: the lock lasts
Cooldownseconds even if you re-run in the same game; that's on purpose. - Two scripts delete each other's key: give each one a different
Id.