rebind is the command-line companion to the Rebind app: scaffold a package,
validate and lint it offline, run it against your local Rebind app with live
logs and hot-reload, then pack, install, and publish it. It also streams app-wide
logs, manages loaded script instances, sends diagnostic reports, and manages the
installed service. Standard setup installs
the optional command-line tools and adds them to your PATH. Custom setup can
omit them.
The CLI owns the package on disk. Everything after publish, the review status,
private or public, withdrawing a version, earnings, and the payout destination,
lives in the app under Marketplace > Publish. See
Publishing scripts.
init, check, lint, pack, and preview work fully offline. service
uses the operating system’s service manager and can run while Rebind is down.
The other commands use the local Rebind app. whoami, hosted ai, publication,
and report need a signed-in session. A publication rehearsal is
offline. Marketplace install requirements are
described under install.
Commands
[path] arguments default to the current directory. Development
commands accept a package directory or a single .lua/.luau file unless
their section says otherwise. script start requires an installed script path.
init
rebind init tools/hello creates
a package named hello), lowercased. With no path the current directory
becomes the package, and files that already exist are kept, so rebind init
beside a main.luau you already wrote adds only the manifest around it. A
directory that already holds a rebind.toml is refused.
The scaffolded rebind.toml carries a commented-out [marketplace] block
(summary, title, category, icon, media, and [marketplace.price]). The
README.md ships inside the .rbp and becomes the listing’s overview when you
publish.
check
Offline validation of the package contract: manifest schema and pricing rules, identity declarations, the fullrequire graph, Luau
compilation of every reachable module, and a permission audit.
The audit covers the two permissions the runtime enforces, net (Net.*) and
exec (System.Exec, System.ExecDetached). It compares uses against the
entry modeline, so a script that declares exec and forgets net fails here
instead of at runtime. Undeclared means all permissions, so for a script that
declares none there is nothing to contradict and the comparison is inert. A
package carrying a [marketplace] block is held to more: publish refuses an
undeclared set, so check fails it here rather than at upload. The
audit lexes the source, so a mention inside a string or comment is not a call,
but it does not resolve aliases (local n = Net; n.Get()).
With a [marketplace] block, check also validates the listing: the shape
and caps of everything the block declares, and for drm = true the two things
a sealed file cannot carry. What must merely be present (a summary, a
screenshot) is publish’s concern. The rules and limits live in
Package format.
Installed and extracted packages
Archives retain the manifest but omit its declared listing media. For an installed or extracted package directory, allow those missing files explicitly:run keeps the same option during hot reload. Without the option,
missing declared media remains an error, so author checks catch missing assets.
publish always requires declared media files and does not accept this option.
lint
Advisory linting over the same require graph, with the Rebind standard library preloaded soHID, Log, hooks, and the rest of the SDK resolve.
Warnings never block anything; error-severity findings (definite bugs) exit
non-zero and block publish.
run
The dev loop: validates, loads the entry into the running Rebind app, streams the script’s log channel, and hot-reloads when a source file changes.Log.* output plus Rebind’s
lifecycle notices and errors for it. --all-logs streams every Rebind log line
(prefixed with its source) for app-wide debugging. Edits to .lua/.luau/
.toml files and anything under assets/ trigger a reload; a failing re-check
keeps the previous run alive.
The watcher owns the instance and its current runtime incarnation. An external
stop or restart, a kill switch, or a changed Rebind process ends that ownership.
The watcher then exits instead of starting the script again on a later edit.
An older Rebind service without ownership metadata is refused before starting
the script.
Reload uses the restart lifecycle, so a failure after the old runtime
stops does not restore it. If the package entry changes, rerun rebind run.
Ctrl+C requests a stop of the owned incarnation. Force-killing the CLI,
including cancelling its subprocess in rebind ai, can leave the script loaded.
Use ps and script stop to inspect and stop it.
Use logs to watch app-wide output without starting a script.
rebind run scripts/wiggle.luau runs a single script from a package’s
scripts/ directory. It inherits the package’s min_sdk, and its requires
resolve from the package root.
For installed directories, see Installed and extracted packages.
exec and repl
Both run chunks in a persistent scratch runtime inside the Rebind app: the full SDK with real input output, state surviving between calls.rebind exec - reads the chunk from stdin. A Lua error exits non-zero.
rebind repl is the interactive version: same runtime, TAB completion queried
live from the VM (your own variables complete, not just SDK names), and
unfinished chunks continue on the next line. ⌃D exits.
pack
Builds the.rbp registry artifact, a gzipped tarball of the package’s
source, manifest, and README. Listing media is uploaded separately at publish
and is not packed. Format details are in
Package format.
--out <path> sets the output file. The default is <slug>.rbp in the current
directory. The path can be any package directory, including one the app
installed under its scripts folder (the location is in
Quickstart).
For omitted listing media, see Installed and extracted packages.
Repacking excludes the root .rebind-market install identity file.
install
Puts a package into the Rebind app’s scripts tree. A.rbp file lands under
packages/<name>/, with the name taken from the archive’s rebind.toml:
@handle/name installs a public marketplace listing the same way the app’s
storefront does: the listing lands under marketplace/. Published free
listings without source protection can be installed as a guest. Paid listings
require a signed-in account entitled to them, not Pro. Protected listings also
have hardware requirements.
A private listing is not reachable this way; install it from its link in the
app.
--force replaces an existing install of the same package or listing.
publish
Check → lint → pack → upload for review. Error-severity lint findings block the upload; warnings do not. Requires:- a package (
rebind.toml); a bare script has no listing to publish - a signed-in account with a claimed
@handle; see publishing requirements for Developer access and source protection - a
[marketplace]section withsummaryandcategory;titleis optional and defaults to the package name - a
[marketplace.price]table for a paid listing; omit it for a free listing - at least one screenshot
.pnginmedia; the first png becomes the cover - an optional
icon, separate from the cover and gallery
.rbp goes up first, followed by the
cover, optional icon, and remaining gallery items in declaration order. For a
DRM listing there is no archive: the require graph is bundled into one sealed
file and sent with the listing, and only the media is uploaded. A README.md
at the package root is sent as the listing’s overview; without one the product
page falls back to summary.
rebind publish --dry-run validates the listing and prepares the archive or
protected-source bundle locally, then prints the proposed terms and artifact
size. It does not sign in, upload, or submit for review. Account access, server
version conflicts, and review decisions remain unchecked.
preview
Loads the package manifest and opens a self-contained local HTML approximation of its marketplace listing from local package files and media, without executing the package. No sign-in or running Rebind app is required.rebind check before publishing for the complete package validation.
rebind preview --json prints structured listing data and embedded local media
without opening a browser. The desktop Assistant uses it for its in-app preview.
Neither preview mode executes the package or simulates its input behavior.
ps
Lists loaded script instances, including their IDs, state, uptime, Luau heap, and average tick duration. It also reports the script sample’s age. A loaded instance can be running, in an error state, or waiting to restart.--watch refreshes the terminal as status arrives. Without --json, it
requires a terminal. --json writes one full status object, including its
scripts array. Together, the flags produce newline-delimited JSON (NDJSON):
one status object per line, starting with the initial snapshot. Pending updates
may be coalesced, so this is a status feed, not a lifecycle event ledger.
Warnings about stale status go to stderr. Ctrl+C or a closed output pipe
exits successfully; a nonterminal --watch without --json exits with code 2.
The per-instance heap is that VM’s Luau allocation, not process RSS or total
memory attributable to the script. Tick duration is an exponential moving
average in microseconds, not a CPU percentage. Rebind process memory, when
available, is reported separately. Missing metrics mean unknown, not zero.
script
Controls one loaded instance through Rebind’s lifecycle manager.start <path> requires a file in Rebind’s installed scripts tree. Use
run for a development file outside that tree. stop, restart, and
inspect take a selector: the exact runtime UUID, an exact path reported in
file or source_path, or a unique display name. UUIDs take precedence.
Names and paths must match exactly; prefixes are not selectors. If more than
one instance matches, the command fails and lists IDs to choose from.
These same selector rules apply to logs --script and /spy script.
stop removes the selected instance without changing its autostart setting.
The existing input-release safety path can release keys or buttons held by
other scripts.
inspect shows sampled metrics, launch origin, runtime incarnation, timer and
coroutine counts, retry attempts, and any error or restart-unavailable reason.
--json returns the selected script object. An incarnation identifies one
runtime lifetime; restarting an instance creates a new incarnation.
Restart behavior depends on the launch origin:
- Disk-backed scripts are read again from disk and pass through current authorization checks, including checks for protected scripts.
- Source-launched scripts replay retained entry source. Retention is limited
to 1 MiB per source and 16 MiB total. Exceeding either limit does not reject
the initial start; it makes replay unavailable, reported by
inspect. - Restart is not transactional. Once the old runtime has stopped, a failure to load the replacement leaves an error state rather than restoring it.
logs
Streams new log lines from Rebind and every running script. Each entry includes the level, source component, and local time when the CLI receives it, not the time the event originally occurred.--level sets the minimum level: debug, info, warn, or error. With no
filter, all received levels appear. debug also includes trace lines from the VM.
Relay debug lines require the Rebind service to run with RUST_LOG=debug;
script Log.Debug lines are published independently of that filter.
--script <selector> resolves a loaded instance using the selector
rules and filters by its runtime ID carried on log records. This
is attribution, not a text search over the source label or message. Lines
without a matching ID are excluded. An instance without runtime attribution
metadata is refused. Without --script, all sources remain visible.
There is no history replay. If Rebind cannot be reached at startup, the command
prints the directory for earlier Rebind log files, or a journalctl command
for a Linux systemd install. Script log history is not retained by Rebind.
Early startup messages are written to the Rebind log file before live
publishing is available.
After connecting, reconnection across a Rebind restart is best effort. Lines
emitted while disconnected are not recovered. Ctrl+C stops the stream.
macro record
Create a temporary Luau helper that prepares a foreground window and records through the relay’s shared macro recorder. By default, a new mouse-button press starts the take:--window is a case-insensitive substring of the foreground window title.
--start-key accepts Mouse1 through Mouse5 and defaults to Mouse1.
--stop-key accepts F1 through F24 and defaults to F1. Choose a stop key
different from the global recorder hotkey. --name defaults to recording.
Before arming, the helper restores the window’s saved position and size and
moves the cursor to desktop coordinates (0, 0). It verifies both before
accepting the start button. Release any held mouse buttons during setup.
The first successful setup captures the window’s current bounds; the command
saves that layout for subsequent recordings matching the same title pattern,
including after graceful cancellation. To choose a different layout, pass
--bounds 60,80,1000,700 for x, y, width, and height. A layout or cursor that
cannot be verified prevents recording from starting.
If the window moves or resizes while armed, setup runs again before a new
press can start the take. The pointer reset is setup, not part of the macro.
Add --start-on-focus to begin recording automatically after setup succeeds,
without waiting for a mouse-button press. This includes the movement from
(0, 0) to your first drawing point. F1 still finishes the take by default.
By default, the first new button-down while the window is foreground starts the take and
is included in it. The stop key finishes the take even after focus changes;
its press and release are excluded from the recording. Release the stop key
to finish the helper. Pressing it before recording starts cancels the helper.
Another caller’s recording is never replaced.
The command reports progress and returns the saved macros/*.luau path.
The relay chooses a free filename. Temporary helper files are removed after
completion or graceful cancellation. If saving fails, the command reports the
retained take’s path. Ctrl+C stops the helper; a cancelled take is discarded.
Force-killing the CLI can leave its helper running, as with rebind run.
This requires input capture and foreground-window support on the host.
Window bounds use native desktop coordinates. On macOS, the menu bar can
prevent a normal window from reaching y=0; use its actual allowed top position
in --bounds. Cursor (0, 0) remains the desktop origin.
A relay started with --no-capture cannot observe the trigger keys. Linux
foreground-window integration is not implemented. For supervising this command
from the app, see Assistant event waits and recovery.
report
Sends a diagnostic report through the running, signed-in Rebind app. See Send logs for review for the sending procedure, confirmation, and bundle contents.service
Checks or controls the installed Rebind service through the operating system’s service manager. It does not install a missing service.status reports TCP reachability. On macOS and Linux it also reports
service-manager state; on Windows it names the scheduled task. It honors
REBIND_PORT_OFFSET; service-manager state still refers to the installed
service and does not prove which process owns the responding ports.
start, stop, and restart act only on the installed service. They refuse
to run if REBIND_PORT_OFFSET, REBIND_DATA_DIR, or ZENOH_ROUTER_ENDPOINT
is present, even if its value is empty or zero. Unset these variables before
managing the installed service. An occupied Rebind TCP port can prevent a
start; stop a manually launched copy where it runs.
stop does not disable auto-start. On macOS, Rebind starts again at your next
GUI login. On Linux, it starts again at the next boot. start brings it back
sooner. On macOS, restart unloads the agent, waits for it to unload, then
loads it again.
Linux service changes use systemctl --no-ask-password, so the CLI does not
open a password prompt. Service-manager errors reach the terminal. If access
is denied, rerun the command with sudo. On WSL without a Rebind systemd
unit, use a Windows shell for the Windows commands.
Troubleshooting exit codes
These codes apply tologs, report, and service:
whoami
mcid is your registered Rebind Link, shown as the same short id the app
displays, with its live attachment state; transport is the session mode
(hardware when your device drives output, software otherwise).
ai
For the app’s separate conversation and execution controls, see Use Assistant. A terminal coding agent for the scripts in the current directory. It reads and edits files, validates them withcheck and lint, runs chunks with exec,
and calls other subcommands, asking before it writes a file or runs a
command. Its CLI tool refuses report; run that command yourself to review
what is sent. The model
runs through your Rebind session: the app must be running and signed in, and
the server puts the full SDK documentation in front of every request, so the
agent writes against the real API.
default and pro, picked with --model or /model inside the
session. Both models draw on one daily AI budget for your account. The status
bar shows how much of today’s budget is used; it resets at 00:00 UTC. Your
AGENTS.md in the directory, if present, is given to the agent as standing
instructions.
Inside the session: Enter sends, Alt+Enter or a trailing \ adds a line,
Esc cancels, Ctrl+C quits. /plan <task> asks for a step list to approve
first, /run [path] runs a script with live logs, /init <name> scaffolds a
package, /resume reloads the last session from .rebind-ai/session.json,
!cmd runs a shell command.
/help lists commands and keyboard controls. Left and Right move the prompt
cursor, Home and End move within its line, and Delete and Backspace remove
characters around the cursor. Up and Down recall history, then restore your
unsent draft. Paste inserts at the cursor. A busy turn or a used-up AI budget
keeps your draft in the prompt. Change models after the current turn finishes or
after cancelling it.
Confirmation previews scroll with PgUp/PgDn or the mouse wheel. Up and Down
select a choice, Enter confirms it, and Esc dismisses the preview. Choices stay
visible while you scroll through a long file, diff, or command.
--ollama <url> or --openai <url> use a local or third-party model instead of
your Rebind session; those receive no documentation from the server and rely on
the lua_api and example tools.
Runtime inspector
Spy opens and starts observing automatically in each AI session. It shows script status, system context, recent logs, and normalized input while the Rebind app is running. Wide terminals use a sidebar; narrow terminals keep a compact summary above the prompt. The panel stays visible during confirmations and when observation is paused or off. In hardware mode, Spy shows the number of peripherals reported as attached, including zero. Devices that are only shared or visible to the host are not counted. Failed USB queries show unavailable; old observations show stale. Software mode has no hardware peripheral count.
Input stays local unless you approve
/spy share. Approval queues that excerpt
for your next user turn, including a /plan request. That turn sends it to the
selected model provider and saves it as chat in .rebind-ai/session.json.
/spy clear and /spy off discard a pending attachment, but do not remove
previously shared messages. The live observation rings themselves are ephemeral
and are not saved or restored by /resume.
While the inspector is enabled, the model can call runtime_context without
another confirmation to read script and system status and recent logs. It
cannot read the input ring through that tool. Tool results enter the conversation
and saved session. Each user turn has a shared 16 KiB evidence budget for input
attachments and all runtime_context results, so repeated calls do not grant
another full budget. Requests that cannot fit the remaining budget fail.
Observation limits
Older records are evicted as buffers fill. Gaps, evictions, and omitted records
mean the view is incomplete. Status age and input age describe observations,
not a guarantee that the runtime is still in that state.
Input records describe normalized events at capture admission, not physical
USB packets or a typed-text transcript. Consecutive movement or scroll events
may be aggregated. Outcomes describe dispatch decisions and attempts, such as
suppression or attempted reinjection; they do not prove that a target application
received the input. Pending input stays visible while dispatch is blocked.
Missing key state after a gap is unknown, not released.
Input observation uses an owner-specific loopback HTTP lease. Only one inspector
can hold it at a time. Without renewal, it expires after five seconds and clears
the relay’s input buffer. The raw trace is not published over Zenoh.
ui
On Windows with the default URL, opens the installed Rebind Desktop app when that optional component is present. Otherwise it opens the control panel with an installed Chromium browser’s app mode, falling back to an ordinary browser tab when needed.--url defaults to http://127.0.0.1:19480 and must stay on loopback: the
app’s WebSocket guard only accepts localhost, 127.0.0.1, and ::1 origins.