Metadata-Version: 2.4
Name: luafend
Version: 2.5.0
Summary: Command line client for Luafend - Lua and Luau source code protection.
Project-URL: Homepage, https://bypass.onl
Project-URL: Documentation, https://luafend.com/docs
Keywords: lua,luau,obfuscator,obfuscation,code-protection,roblox
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# Luafend

Command line tool **and** Python library for [Luafend](https://bypass.onl) — source
protection for Lua and Luau. One package: protect a script from your terminal, from a build
script, from CI, or from your own Python code.

Targets **Luau**, **Lua 5.1** and **Lua 5.4**, covering Roblox, Garry's Mod, LuaJIT,
embedded Lua and standalone hosts.
## Install

```bash
pip install luafend
```

Or without installing it into your project:

```bash
pipx install luafend
```

Requires Python 3.9 or newer. Standard library only, no dependencies.

## Two ways to run the command line

Type **one word** — in Command Prompt or PowerShell on Windows, in Terminal on macOS and
Linux — and Luafend opens its own shell. Inside it, every command starts with a slash:

```
luafend

luafend ~ > /login
luafend ~ > /obfuscate main.lua --mode maximum
luafend ~ > /exit
```

Tab completes commands, files, flags and values. Typing `--` suggests the flags that
command accepts. `/help` lists everything.

Or drop the slash and run the same command straight from your own shell. Nothing opens,
the job runs, you get your prompt back — this is the form a build script or CI job uses:

```bash
luafend login
luafend obfuscate main.lua --mode maximum --lua luau
```

## Getting started

```bash
luafend login                    # opens your browser, no password typed
luafend obfuscate main.lua       # writes main.obf.lua
```

The first run asks for the mode and the Lua version and remembers both. Every run after
that is silent, and prints what it used and where the setting came from.

## Use it from Python

The short way: set it on the package, then run.

```python
import luafend

luafend.key = "luf_YOUR_KEY"
luafend.mode = "maximum"
luafend.lua = "luau"

result = luafend.run('print("hello")')

if result.ok:
    print(result.output)
else:
    print(result.error)
```

Set `luafend.out = "main.obf.lua"` and the file is written for you as UTF-8. Set
`luafend.source` and `run()` needs no argument at all.

These settings live on the package, so there is one set of them per program. That is
right for a script and wrong for concurrent work; for a web server use a client, which
carries its own:

```python
client = luafend.Luafend(key="luf_YOUR_KEY")
client.mode = "maximum"
result = client.run(source)
```

Or describe a single build one line at a time:

```python
import luafend

job = luafend.Job()
job.key = "luf_YOUR_KEY"
job.source = 'print("hello")'
job.mode = "maximum"
job.lua = "luau"

result = job.run()

if result.ok:
    print(result.output)
else:
    print(result.error)
```

`run()` never raises. Every Result also carries the measurements, with no flag to switch
on:

```python
print(result.size, "bytes")        # protected script
print(result.source_size, "bytes") # what you sent
print(result.seconds)              # how long it took
print(result.timestamp)            # when it finished, UTC
print(result.quota_left)           # obfuscations left this month
```

Set `job.out = "main.obf.lua"` and the file is written for you as UTF-8, line endings
intact.

If you just want the string and are happy for a failure to stop the program, there is a
one line form that raises `luafend.LuafendError` instead:

```python
print(luafend.obfuscate('print("hello")', key="luf_YOUR_KEY", mode="maximum", lua="luau"))
```

Leave `key` out and `LUAFEND_TOKEN` is used, then the account from `luafend login`.
`mode` is `lite`, `balanced` or `maximum`; `lua` is `luau`, `lua-5.1` or `lua-5.4`.

## Commands

| Command | What it does |
|---|---|
| `login` | Sign in through your browser |
| `logout` | Sign out and revoke this machine's token |
| `whoami` | Name, email, sign-in provider, plan |
| `billing` | Plan, credits, monthly usage and limits |
| `obfuscate <file>` | Protect one script |
| `batch <pattern>` | Protect many files at once |
| `settings` | Show, reset or write project settings |
| `cd [folder]` | Show or change the current folder |
| `update` | Check for a newer version and install it |
| `docs` | Open the documentation |
| `version` | Show the CLI version |

## Options

| Flag | Meaning |
|---|---|
| `--mode lite\|balanced\|maximum` | How much protection. Lite is fastest, Maximum compiles the script into its own virtual machine |
| `--lua luau\|lua-5.1\|lua-5.4` | Which Lua the result has to run on |
| `-o, --out <path>` | Where to write. A folder for `batch`, a file for `obfuscate` |
| `--pick` | Choose mode and version again instead of using the saved ones |
| `--force` | Overwrite an existing output file |
| `--stdout` | Print the result instead of writing a file |
| `--all` | `batch` only: rebuild every file, including unchanged ones |

Without `--lua`, the version is guessed from the file: a `.luau` extension or Roblox
globals such as `game:GetService` mean Luau. The guess is always printed.

## Protecting many files

```bash
luafend batch "src/**/*.lua" -o dist
```

`*` and `?` never cross a path separator, `**` matches any number of directories. A plain
folder means every `.lua` and `.luau` inside it. Files that have not changed since their
last build are skipped, so a rebuild does not spend your monthly quota twice. Requests are
paced to your account's rate limit.

## Project settings

`luafend.json` pins how a project builds, so everyone working on it gets the same result:

```json
{
  "mode": "maximum",
  "lua": "luau",
  "out": "dist",
  "include": ["src/**/*.lua"]
}
```

It is searched for upwards from the current folder, so any subfolder of the project
behaves the same. With an `include` list, plain `luafend batch` is enough.

`luafend settings` shows what applies and where each value came from. `luafend settings
init` writes a starter file next to your token, which then applies everywhere you have no
project file.

## Signing in

`login` never asks for a password. It opens a consent page in the browser where you are
already signed in and waits for approval. The token it receives is stored in your OS
config directory, never in the working directory:

| | |
|---|---|
| Windows | `%APPDATA%\luafend\config.json` |
| macOS | `~/Library/Application Support/luafend/config.json` |
| Linux | `~/.config/luafend/config.json` |

On macOS and Linux the file is created with mode `0600`. `logout` revokes the token on the
server as well as deleting it here.

The token is only ever sent to the official Luafend API or to a server on your own machine.

## In CI

Set `LUAFEND_TOKEN` instead of signing in. It takes priority over the config file, so no
login step is needed:

```yaml
- run: pip install luafend
- run: luafend batch "src/**/*.lua" -o dist --mode balanced --lua luau
  env:
    LUAFEND_TOKEN: ${{ secrets.LUAFEND_TOKEN }}
```

Without a terminal, nothing is ever asked interactively: a missing setting fails with the
flag it needs named, rather than blocking on a prompt nobody can answer.

Exit codes:

| Code | Meaning |
|---|---|
| `0` | Success |
| `1` | Error |
| `2` | Not signed in, or the session expired |
| `3` | Out of credits, or the plan does not allow that mode |

## Environment

| Variable | Effect |
|---|---|
| `LUAFEND_TOKEN` | Use this token instead of the stored one |
| `LUAFEND_API` | Point at a different API host, for local development |
| `NO_COLOR` | Turn colour off |
| `LUAFEND_ASCII=1` | Plain ASCII instead of box drawing characters |

## Links

- Website — https://bypass.onl
- Documentation — https://bypass.onl/docs
- The same client for Node.js — https://www.npmjs.com/package/luafend
