> ## Documentation Index
> Fetch the complete documentation index at: https://gabibou-ui.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Key systems and providers

> Connect a key gate to Luarmor, Vampauth, KeyAuth, a custom provider, or a server validator.

> [!WARNING]
>
> A key gate is client-side UI, not authentication. A client can be modified or bypassed. Keep keys and validation rules on the server, and check authorization for every protected server action.
>
> ## Choose a provider
>
> `UI:KeyProvider(name, options)` adapts a provider SDK into the `Validate(key)` callback used by the gate. It does not fetch or initialize provider code for you: load the SDK explicitly in your environment, then pass the returned SDK object or callbacks. The [provider examples](https://gabibou-ui.mintlify.site/examples/key-providers.md) show complete setups for Luarmor, Vampauth, KeyAuth, and a custom validator.
>
> | Use this when… | Provider | Note |
> | - | - | - |
> | You use Luarmor's public key-check SDK | `Luarmor` | Only `KEY_VALID` accepts; use one SDK table per `ScriptId`. The adapter does not call `load_script()` or set `script_key`. |
> | You use the Vampauth client SDK | `Vampauth` | `AuthSecret` is present in the client script and can be inspected; obfuscation does not make it private. |
> | You already use a KeyAuth-compatible Lua SDK | `KeyAuth` | Supply your own init/license bridge. The current KeyAuth docs say their Lua example uses an older API version; see their [initialization](https://docs.keyauth.cc/api/getting-started/initialization), [license login](https://docs.keyauth.cc/api/getting-started/license/license-login), and [SDK examples](https://docs.keyauth.cc/overview/examples). |
> | You have another provider or endpoint | `Custom` | Write a `Validate(key)` callback that maps its success and rejection to Gabibou UI's boolean result. |
>
> The `GetKey` button is separate from validation. Set it to your provider's purchase, redeem, or key-request URL; Gabibou UI only offers the link for copying and does not open it or check that it works. For a Roblox experience, [the external server example](https://gabibou-ui.mintlify.site/examples/key-external-server.md) shows a generic HTTP bridge that keeps provider credentials on the server. The [server validation example](https://gabibou-ui.mintlify.site/examples/server-validation.md) shows an in-game key list instead.
>
> Provider SDKs in executor/client environments can be inspected or changed by the client. Do not treat an accepted gate as proof that a player may perform a protected server action. Never put a provider's admin API key or server-only credential in a distributed client script.

## Key validator contract

The rc.17 access screen places a credential well after the input and before the Verify action. The Get key and Cancel actions remain secondary 44 px controls. Automatic card heights remain 332 px without Discord/GetKey, 394 px with Discord only, 404 px with GetKey only, and 462 px with both. The logo can show the player's avatar with `Logo = "Avatar"`, or use `Logo = false` and `MarkText`.

`UI:CreateKeySystem(options)` requires `Validate(key)`. The validator can yield and must return `true, result` for acceptance or `false, message` for rejection. `gate:Submit(key)` starts validation asynchronously. Raw validator exceptions and submitted keys are not shown in the gate's generic error status.

```lua theme={null}
local gate = UI:CreateKeySystem({
    Title = "Access key",
    Subtitle = "Enter your key to continue.",
    Logo = "Avatar",
    BackgroundTransparency = 0.42, -- full-screen dimmer
    CardTransparency = 0.1, -- key dialog surface
    MarkText = "G",
    SubmitText = "Verify key",
    Validate = function(key)
        return validateKey:InvokeServer(key)
    end,
    OnSuccess = function(gate, result)
        print("Access granted")
    end,
    OnCancel = function(gate)
        print("Key entry cancelled")
    end,
})
```

![Live rc.21 key gate with an access-key field, Verify, Get key, Cancel, and Discord actions](https://mintcdn.com/gabibou-ui/I5Uhr5uQ-CiiLlxQ/images/keygate.png?fit=max\&auto=format\&n=I5Uhr5uQ-CiiLlxQ\&q=85\&s=5d696f17515a7636e1f27be4c18212c5)

*A live rc.21 screenshot of the key-gate layout. It shows the available actions; it does not demonstrate key validation or link-copy behavior.*

The key gate shows the local player's avatar by default with `Logo = "Avatar"`. Set `Logo = false` to show `MarkText`, or pass an uploaded image URI such as `rbxassetid://123456`. `gate:GetParts()` exposes `MarkImage` and `MarkText` for the logo and `CredentialWell` for the inset background behind the access-key input. Style `CredentialWell` directly. The submit button defaults to `Verify key`.

Named key-gate parts use responsive layout by default. `gate:SetPartLayout(name, options)` stores a `Position`, `Size`, `AnchorPoint`, or `Visible` override across reflow and updates; `gate:ResetPartLayout(name)` restores automatic placement. The fixed `Overlay` and `Card` cannot be moved this way. See the [Design guide](https://gabibou-ui.mintlify.site/customization/design-your-interface.md) for a short Get key button example.

The default width is 420 px (280–600). Automatic heights are 332 px without Discord or GetKey, 394 px with Discord only, 404 px with GetKey only, and 462 px with both. `Height` accepts 280–560 px. When the height is automatic, adding or removing Discord/GetKey recalculates it; an explicitly supplied height remains fixed during updates. The layout gives the Verify action a full-width row when GetKey is present, followed by Get key and Cancel actions.

`BackgroundTransparency` controls the full-screen dimmer and defaults to `0.42`. `CardTransparency` controls only the key dialog surface and defaults to `0.1`. Both accept values from 0 (opaque) to 1 (transparent); change them with `gate:Update({...})`.

## Choose action labels

Use short labels when possible. In rc.20, a label that does not fit its button shows an ending ellipsis; its configured string remains unchanged. `SubmitText` and `CancelText` may be up to 32 characters, while `GetKey.Text` and `Discord.Text` may be up to 48:

```lua theme={null}
local keyOptions = {
    SubmitText = "Verify membership",
    CancelText = "Return to menu",
    GetKey = {
        Url = "https://example.com/get-key",
        Text = "Request a new access key",
    },
    Discord = {
        Url = "https://discord.gg/your-server",
        Text = "Copy community invite",
    },
}
```

Copy these fields into your existing `UI:CreateKeySystem({...})` options table, including its required `Validate` callback. Label truncation is visual; it adds no API or font-scaling setting.

## Get a key

`GetKey` is disabled when omitted or set to `false`. A URL string or `{Url, Text, Copy}` table adds a button that attempts to copy a provider link. URLs must be HTTPS, include a host, contain no whitespace or control characters, and be at most 2048 bytes. `Text` defaults to `Get key` and is limited to 48 characters. The optional `Copy(url)` callback can provide clipboard support. If copying is unavailable, fails, or returns `false`, the gate shows the URL in a selectable field for manual copying. The UI never opens the link automatically.

```lua theme={null}
GetKey = {
    Url = "https://example.com/get-key", -- Replace with your provider URL.
    Text = "Get key",
    -- Optional clipboard implementation:
    -- Copy = function(url) setclipboard(url); return true end,
}
```

For a custom action, set `GetKey = function(gate) ... end`. Call `gate:GetKey()` to run it; this returns a success boolean and optional reason. The action is single-flight. Update/destroy invalidates any stale result from a yielding action. `gate:Update({GetKey = false})` removes the button. `GetParts()` exposes `GetKeyButton`, `KeyLink`, and `KeyLinkStatus`.

When a window already exists in the same scope, a custom action can use it to show the provider instructions:

```lua theme={null}
GetKey = function(gate)
    window:Notify({
        Title = "Get a key",
        Content = "Visit the provider and return here to enter your key.",
        Kind = "info",
    })
end
```

The built-in UI demo uses a custom callback to display its sample key, `demo`, in a notification. That callback is only a local preview; it does not replace server validation.

## Discord invite button

`Discord` can be an invite URL string or a table with `Url`, `Text`, `CopiedText`, `Color`, optional image `Logo`, and optional `Copy(url)` callback. Supported invite forms are `https://discord.gg/<code>` and `https://discord.com/invite/<code>`. The UI does not call Discord or check whether an invite is active.

```lua theme={null}
Discord = {
    Url = "https://discord.gg/your-server",
    Text = "Copy our Discord invite",
    CopiedText = "Invite copied",
    Color = Color3.fromRGB(88, 101, 242),
}
```

Set `Discord = false` to remove the button. `gate:CopyDiscordLink()` returns a boolean and an optional reason. Without a working clipboard function, the dialog shows a selectable link for manual copying. `gate:Update({Discord = false})` removes Discord while the gate is idle and open. `GetParts()` exposes the Discord button, icon, selectable link, and status label.

## Gate lifecycle and options

`gate:Cancel()` cancels the current attempt but leaves the dialog open. `gate:Destroy()` closes it and silences future callbacks. `gate:Update(options)` validates and applies partial options while key validation is not running; it refuses updates during validation or after completion/destruction. An update may invalidate a pending `GetKey` action and suppress its stale result.

Key dialog text defaults include `Title = "Access key"`, `Subtitle = "Enter your key to continue."`, `Placeholder = "Enter key…"`, `InputLabel = "Access key"`, `SubmitText = "Verify key"`, `ValidatingText = "Verifying…"`, `CancelledText = "Cancelled"`, and `MarkText = "G"`. `MaxLength` defaults to 256 and accepts 1–4096. See [API reference](https://gabibou-ui.mintlify.site/reference/api.md) for the rest of the gate options.

## Use `Launch` for startup

`UI:Launch(spec)` combines declarative window creation with `KeySystem`, `Loading`, sequential `Steps`, and lifecycle callbacks. The gate appears first. On success the library runs each setup step, then builds the window.

```lua theme={null}
local session = UI:Launch({
    Id = "Settings",
    Title = "Settings",
    KeySystem = {
        Validate = function(key)
            return validateKey:InvokeServer(key)
        end,
        Discord = {Url = "https://discord.gg/your-server"},
    },
    Loading = {Title = "Preparing", Variant = "Centered", Height = 300},
    Steps = {{Title = "Load preferences", Run = function(loader)
        -- Load application data here.
    end}},
    Tabs = {{Title = "Home", Icon = "home"}},
    OnReady = function(window, currentSession)
        window:Notify({Title = "Ready", Content = "Setup is complete.", Kind = "success"})
    end,
    OnError = function(message)
        warn(message)
    end,
    OnCancel = function(currentSession)
        print("Startup cancelled", currentSession.Status)
    end,
})
```

The session exposes `Status`, `LastError`, `Gate`, `Loader`, `Window`, `Cancel()`, and `Destroy()`. `Cancel()` calls `OnCancel` once. `Destroy()` releases resources without calling that callback. Status values are `WaitingForKey`, `Loading`, `Ready`, `Failed`, and `Destroyed`.

See [Discord key example](https://gabibou-ui.mintlify.site/examples/key-discord.md) and [server validation example](https://gabibou-ui.mintlify.site/examples/server-validation.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.