# Native programs

The `libremetaverse-programs` package owns one native Rust binary for every
program in the pinned LibreMetaverse source snapshot. The source inventory and
hashes remain in `upstream-programs.json`; implementation status is tracked
here so a source entry is never mistaken for a completed port.

| Binary | Upstream project | Status |
| --- | --- | --- |
| `osd-inspector` | OSDInspector | Implemented and tested offline |
| `simple-bot` | SimpleBot | Implemented with live and deterministic fake-grid modes |
| `packet-dump` | PacketDump | Implemented with live and deterministic fake-grid capture |
| `prim-inspector` | PrimInspector | Implemented with live and deterministic fake-grid discovery |
| `inventory-explorer` | InventoryExplorer | Implemented with live inventory and deterministic AIS fixtures |
| `irc-gateway` | IRCGateway | Implemented with live IRC/grid transports and deterministic offline scripts |
| `test-client` | TestClient | Native shell, registry, system, and communication groups implemented; remaining command groups tracked by #92–#94 |
| `vivox-test` | VivoxTest | Pending milestone 11 issue #95 |
| `webrtc-test` | WebRtcTest | Pending milestone 11 issue #96 |

## OSDInspector

`osd-inspector` is a bounded, offline command-line client of the public native
StructuredData and Primitive APIs. It does not initialize a grid client, read
credentials, load a native codec, or invoke a .NET process.

```text
osd-inspector inspect <file>                 # alias: i
osd-inspector convert <input> <format> <out> # alias: c
osd-inspector validate <file>                # alias: v
osd-inspector prim-to-osd
osd-inspector osd-to-prim <file>
```

The supported output formats are `json` (`j`), `xml` (`x`), `binary`
(`bin` or `b`), and `notation` (`llsd` or `n`). Use `-` as an input or output
path for standard input or standard output. Format detection uses the filename
extension as a hint and then checks every native parser, so binary and notation
LLSD work through files and pipes as well as JSON and XML.

Input is read through a bounded buffer and is limited to the StructuredData
binary allocation limit by default. `--max-input-bytes <BYTES>` can lower that
ceiling for constrained callers. StructuredData also enforces its depth, node,
and aggregate allocation limits while parsing.

Normal results are written to stdout and diagnostics to stderr. Exit status is
stable for scripts:

| Status | Meaning |
| --- | --- |
| 0 | Success |
| 2 | Command-line usage error |
| 3 | File or standard-stream I/O error |
| 4 | Invalid or oversized OSD input |
| 5 | Primitive conversion or output serialization error |

Run the issue-focused CLI suite and the related translated StructuredData cases
with:

```sh
cargo test -p libremetaverse-programs --test osd_inspector_cli --locked
cargo test --manifest-path tests/compat/Cargo.toml --test structured_data --locked
```

## SimpleBot

`simple-bot` is an asynchronous native Rust client with the command surface of
the upstream example. A live session accepts the original positional
credentials, or reads them from the environment:

```text
simple-bot FIRSTNAME LASTNAME PASSWORD [--login-uri URL]
GRID_FIRST_NAME=... GRID_LAST_NAME=... GRID_PASSWORD=... simple-bot
```

`GRID_LOGIN_URL` supplies the endpoint when `--login-uri` is absent, and
`--login-timeout-seconds` bounds login to 30 seconds by default. Credentials,
authorization values, capability URLs, and token values are redacted from
output. After login the bot answers `help`/`?`, `where`/`location`, `sit`,
`stand`, `dance`, `fly`, `walk`, `jump`, and `hello`/`hi`/`hey` instant
messages. It also greets other avatars that say hello in local chat. Ctrl-C
cancels pending greetings and login work, releases an in-progress jump,
unsubscribes event handlers, logs out, and disposes the client before exit.

For offline validation, `--fake-script FILE` runs the same command handlers
against a deterministic fake grid. Scripts contain no credentials. Blank
lines and lines beginning with `#` are ignored; remaining lines use one of:

```text
im<TAB>source-uuid<TAB>source-name<TAB>message
chat<TAB>source-uuid<TAB>source-name<TAB>message
status<TAB>message
```

The reader accepts at most 1 MiB, 1,024 events, 256-byte names, and 4,096-byte
messages. The fake transcript records every client call, uses the real DANCE1
UUID, and finishes with zero active tasks and sockets. Run the issue-focused
suite and the related runtime compatibility cases with:

```sh
cargo test -p libremetaverse-programs --test simple_bot_cli --locked
cargo test --manifest-path tests/compat/Cargo.toml --test core_runtime_shims --locked
```

## IRCGateway

`irc-gateway` preserves the upstream seven-position live interface and bridges
fully audible normal local chat to one IRC channel in both directions:

```text
irc-gateway FIRSTNAME LASTNAME PASSWORD MASTER_UUID IRC_HOST IRC_PORT '#channel'
```

The positional values can instead come from `GRID_FIRST_NAME`,
`GRID_LAST_NAME`, `GRID_PASSWORD`, `GRID_MASTER_UUID`, `IRC_HOST`, `IRC_PORT`,
and `IRC_CHANNEL`. `GRID_LOGIN_URL` or `--login-uri` selects a grid endpoint.
`--nickname` defaults to the upstream `SLGateway` name. Login is bounded by
`--login-timeout-seconds`; `--messages-per-second` and `--burst` control the
per-direction token buckets.

The native IRC transport registers with `NICK`/`USER`, responds to `PING`,
joins after numeric `001`, handles fragmented UTF-8 input, caps inbound and
outbound lines, and reconnects with exponential backoff. The bridge uses
bounded queues, sanitizes names and line breaks, suppresses self messages,
short-window duplicates, and reflected cross-transport messages, and accepts
teleport lures only from `MASTER_UUID`. Login messages and console output
redact credentials, tokens, capability URLs, and other URLs. Ctrl-C cancels
login and reconnect waits, closes the IRC socket, joins workers, unsubscribes
grid callbacks, logs out, and disposes the native client.

Offline tests use the same routing and rate-limit engine without credentials or
network access:

```text
irc-connect
irc-disconnect
irc<TAB>target<TAB>nickname<TAB>message
grid-chat<TAB>source-uuid<TAB>name<TAB>normal|other<TAB>full|other<TAB>message
grid-login<TAB>message
teleport<TAB>source-uuid<TAB>session-uuid
advance<TAB>milliseconds
shutdown
```

Scripts are limited to 1 MiB and 4,096 events; names, messages, transport
queues, and loop histories are also bounded. Run the issue-focused suite and
the related native social-message compatibility cases with:

```sh
cargo test -p libremetaverse-programs --test irc_gateway_cli --locked
cargo test -p libremetaverse-programs irc_gateway::tests --locked
cargo test --manifest-path tests/compat/Cargo.toml --test social_message_semantics --locked
```

## TestClient

`test-client` is a native asynchronous multi-avatar command shell. It accepts
one account with `--first`, `--last`, and `--pass`, or a bounded account file
with `--file`. Account-file records use `First Last Password` followed by an
optional `Region/x/y/z` start location. `--loginuri`, `--startpos`, `--master`,
`--masterkey`, `--groupcommands`, `--scriptfile`, and `--nogui` preserve the
upstream shell controls. Each login has a configurable timeout, the registry is
limited to 64 clients, and `@ First Last` selects one client while `@` restores
broadcast command routing.

The implemented command groups are `@`, `debug`, `echomaster`, `help`, `im`,
`imgroup`, `load`, `login`, `logpacket`, `logout`, `md5`, `quit`, `say`,
`setmaster`, `setmasterkey`, `shout`, `showeffects`, `sleep`, `waitforlogin`, and
`whisper`. Chat and instant-message bodies are bounded to the grid protocol
limit. Group commands require both `--groupcommands` and current group
membership. Master chat can be echoed, master teleport lures are accepted, and
remote `login` and `md5` command text is redacted from transcripts. Packet logs
contain only timestamped packet type, simulator name, and byte count; they are
limited to 10,000 records and 16 MiB.

The native `load` command reads a portable command-alias manifest instead of a
CLR assembly. Each non-comment line is tab-separated
`name<TAB>description<TAB>template`; `{args}` or `$*` inserts the quoted command
arguments. Manifests are limited to 1 MiB and 128 commands, with an alias
expansion depth of eight.

Use `--fake-script FILE` for deterministic, credential-free terminal and grid
validation. Normal lines are dispatched exactly like interactive input. Fake
grid records begin with `!` and use tab-separated fields:

```text
!client<TAB>client-uuid<TAB>first<TAB>last
!client<TAB>client-uuid<TAB>first<TAB>last<TAB>master-name<TAB>master-uuid<TAB>true|false
!person<TAB>avatar-name<TAB>avatar-uuid
!group-member<TAB>avatar-uuid
!chat<TAB>client-uuid<TAB>source-uuid<TAB>name<TAB>message
!im<TAB>client-uuid<TAB>source-uuid<TAB>name<TAB>agent|object|teleport<TAB>true|false<TAB>message<TAB>session-uuid
!packet<TAB>client-uuid<TAB>packet-type<TAB>simulator<TAB>bytes
!effect<TAB>client-uuid<TAB>summary
!disconnect<TAB>client-uuid<TAB>reason
!cancel
!shutdown
```

Inputs are limited to 1 MiB and 4,096 lines. The fake transcript records native
backend calls and ends with connected-client, pending-login, active-task,
shutdown, dropped-event, and pending-command-inventory state. Run the focused
framework and compatibility checks with:

```sh
cargo test -p libremetaverse-programs --test test_client_cli --locked
cargo test -p libremetaverse-programs test_client::tests --locked
cargo test --manifest-path tests/compat/Cargo.toml --test core_runtime_shims --locked
cargo test --manifest-path tests/compat/Cargo.toml --test social_message_semantics --locked
cargo test --manifest-path tests/compat/Cargo.toml --test network_semantics --locked
```

## PacketDump

`packet-dump` preserves the upstream live arguments and its 20-second login
timeout while adding safe, bounded capture controls:

```text
packet-dump FIRSTNAME LASTNAME PASSWORD SECONDS
  [--direction incoming|outgoing|both]
  [--packet-type NAME]... [--raw]
  [--output FILE] [--max-output-bytes BYTES] [--max-packets COUNT]
```

`SECONDS=0` captures until Ctrl-C. Credentials can instead come from
`GRID_FIRST_NAME`, `GRID_LAST_NAME`, and `GRID_PASSWORD`; `GRID_LOGIN_URL` or
`--login-uri` selects a login endpoint. The client disables multiple simulator
connections and sends zero land, wind, and cloud throttles like the source
program. Incoming decoded callbacks retain their original datagrams, while
outgoing callbacks are decoded through the public packet factory. Records show
direction, packet type, simulator, byte count, sequence, frequency, ID, and
header flags. Exact packet-type filters may be repeated.

Raw hexadecimal output is opt-in. Live capture masks the password and native
session identifiers wherever they occur in a datagram, and suppresses raw
payloads containing URLs, authorization terms, capability terms, or token
assignments. Login and disconnect messages use the same text redaction.
Output defaults to stdout, may be redirected to a newly truncated file, and is
bounded to 16 MiB and 100,000 matching packets unless lower or higher explicit
limits are supplied. A complete line is either written or rejected at the byte
limit.

Offline validation uses `--fake-script FILE` with records of this form:

```text
incoming<TAB>simulator<TAB>hex-bytes
outgoing<TAB>simulator<TAB>hex-bytes
```

Scripts are capped at 8 MiB and individual datagrams at 64 KiB. Each datagram
is passed through the native wire decoder; malformed and unknown packets are
reported without aborting the capture. The issue-focused and related translated
wire tests are:

```sh
cargo test -p libremetaverse-programs --test packet_dump_cli --locked
cargo test -p libremetaverse --test packet_wire --locked
cargo test --manifest-path tests/compat/Cargo.toml --test wire_semantics --locked
```

## PrimInspector

`prim-inspector` preserves the upstream positional login and search arguments:

```text
prim-inspector FIRSTNAME LASTNAME PASSWORD [SEARCH_TERM]
GRID_FIRST_NAME=... GRID_LAST_NAME=... GRID_PASSWORD=... prim-inspector
```

The native client disables multiple-simulator connections, allows 30 seconds
for login, collects object updates for two seconds, and snapshots the current
simulator's real primitive table. It requests missing properties through
`ObjectManager::SelectObject`, sharing a three-second deadline across at most
1,024 outstanding requests. A disconnect, simulator change, or Ctrl-C cancels
the operation and still removes handlers, logs out, and disposes the client.

Names are filtered case-insensitively after available properties are hydrated.
Matches are ordered by distance from the agent, with local ID and UUID tie
breakers, and the nearest ten are shown by default. `--limit` accepts one
through 100 objects. Output covers transforms, construction data, flags,
parentage, sculpt, light, flexible, ownership, creator, description, sale, and
hover-text details. Status text, descriptions, and names suppress URLs and
credential-like assignments, and `--max-output-bytes` bounds the complete
buffer before anything is written.

For offline validation, `--fake-grid FILE [--search SEARCH_TERM]` reads an LLSD
JSON map containing `region`, `agent_position`, and an `objects` array. Object
maps use the public `Primitive::from_osd` fields and may add `owner`, `creator`,
`sale_type`, `sale_price`, `hover_text`, and `property_state`. The latter is
`ready`, `delayed`, or `missing`; delayed and missing fixtures record the same
selection/property-timeout decisions as a live session. Input is limited to 8
MiB, 100,000 objects, LLSD depth/allocation limits, nonzero UUID/local IDs, and
validated property/sale states. No live grid is contacted by the tests.

Run the isolated CLI golden tests and the related translated primitive cases:

```sh
cargo test -p libremetaverse-programs --test prim_inspector_cli --locked
cargo test --manifest-path tests/compat/Cargo.toml --test world_object_semantics --locked
```

## InventoryExplorer

`inventory-explorer` preserves the source program's live login and operation
options:

```text
inventory-explorer FIRSTNAME LASTNAME PASSWORD [--stats]
  [--search TERM [--type TYPE]] [--export FILE]
```

Credentials may instead come from `GRID_FIRST_NAME`, `GRID_LAST_NAME`, and
`GRID_PASSWORD`; `GRID_LOGIN_URL` or `--login-uri` selects the endpoint. Login
and inventory readiness each have a 30-second deadline. The native client waits
for a populated root in the real `InventoryManager::store`, handles Ctrl-C and
disconnects, removes event handlers, logs out, and disposes the client on every
post-construction path.

With no operation option, the program prints the sorted top-level folders and
items. `--stats` traverses the hierarchy and reports item and root-folder counts
plus the ten most common asset types. `--search` performs a case-insensitive
name search across folders and items, reports parent folders and UUIDs, and
shows link targets. `--type` accepts every native `AssetType` name and narrows
item results. Search output defaults to 50 results and is capped at 1,000.

`--export FILE` writes a deterministic `deterministic-v1` hierarchy with
folders before items, stable case-insensitive name and UUID ordering, asset
types, link targets, and UUIDs. The destination must be an explicit non-symlink
path of at most 4,096 characters. The in-memory export is validated before the
file is written and defaults to a 16 MiB limit. Traversal defaults to 100,000
entries and 64 folder levels, detects cycles, and exposes configurable bounded
limits. Standard output is buffered and limited to 4 MiB, and inventory/status
text redacts URLs and credential-like assignments.

Offline validation uses `--fake-ais FILE`. The file is an LLSD JSON AIS response
with a nonzero `root_id` and ordinary `_embedded.categories`, `.items`, and
`.links` maps. It is capped at 8 MiB and parsed by the public native
`InventoryAISClient`; parsed folders, items, and links are then installed
through the real manager-owned `Inventory` store. This exercises AIS typing,
derived inventory items, link targets, parent indexes, and folder contents
without contacting a grid or embedding credentials.

Run the isolated CLI/golden suite and its related translated inventory gates:

```sh
cargo test -p libremetaverse-programs --test inventory_explorer_cli --locked
cargo test --manifest-path tests/compat/Cargo.toml --test inventory_store_semantics --locked
cargo test --manifest-path tests/compat/Cargo.toml --test inventory_ais_semantics --locked
```
