# StarBonding CLI 1.07

`starbonding-cli.exe` is the public Windows client for StarBonding account
authentication, bonded RTMP ingest, and restream management.

Designed by Star Video  
[starbonding.com](https://starbonding.com) · [starvideo.in](https://starvideo.in)  
Support: [support@starbonding.com](mailto:support@starbonding.com)

The executable must retain the filename `starbonding-cli.exe`; it refuses to
run under another name. This detects accidental renaming, not binary tampering.
No verified public OEM redistribution and branding-policy page currently
exists, so contact support for the applicable terms.

## Help, version, and quick start

```powershell
starbonding-cli.exe --help
starbonding-cli.exe --version
$password | & 'C:\Program Files\StarBonding\starbonding-cli.exe' `
  --username YOUR_ACCOUNT_ID --password-stdin
```

`--help` prints the complete usage text. `--version` prints the installed CLI
version and product identity. Both include Star Video and the two websites.

After authentication, wait for this complete stdout line before publishing:

```text
EVENT READY url=rtmp://127.0.0.1:1935/live/starbonding listener=all dual_stack=true
```

Publish to `rtmp://127.0.0.1:1935/live/<stream-key>`. Any nonempty stream key
is accepted, but only one RTMP ingest may be active.

## Credentials and authentication

Prefer `--password-stdin`; its first input line is the password. For
`--restream-add --restream-url-stdin`, the second line is the URL. Supplying
`--password VALUE` may expose it in command history and process inspection.
`--remember` stores an accepted credential in Windows Credential Manager and
`--forget` deletes it.

```powershell
$password | .\starbonding-cli.exe --username YOUR_ACCOUNT_ID `
  --password-stdin --authenticate-only
```

Success is written to stdout:

```text
AUTH_OK minutes=1440 kb=0 restreams=2
```

Authentication errors are written to **stderr** as `AUTH_ERROR ` followed by a
human-readable message. The message is not a stable machine protocol; use the
exit code. It does not contain the password.

## Public restream JSON contract

Every recognized restream command writes exactly one complete UTF-8 JSON object
and newline to **stdout**, including argument and runtime failures. It never
prints private server responses or individual pagination pages. Diagnostics
may additionally be written to stderr.

Every object has `schema` (always `starbonding.restream.v1`), boolean `success`,
and an `action`: `get`, `add`, `activate`, `deactivate`, or `delete`.

### Get destinations

```powershell
$password | .\starbonding-cli.exe --username YOUR_ACCOUNT_ID `
  --password-stdin --restream-get
```

Success:

```json
{"schema":"starbonding.restream.v1","success":true,"action":"get","destinations":[{"restreamid":42,"active":true,"destination":"rtmp://destination.example/live/PRIVATE_KEY","ingestip":"203.0.113.20","source":"SOURCE","ingestip2":"203.0.113.10"}],"count":1}
```

Empty success:

```json
{"schema":"starbonding.restream.v1","success":true,"action":"get","destinations":[],"count":0}
```

| Destination field | Type | Meaning |
|---|---|---|
| `restreamid` | unsigned integer | Nonzero destination identifier. |
| `active` | boolean | Whether delivery is enabled. |
| `destination` | string | Destination URL; treat it as secret. |
| `ingestip` | string | Server-selected ingest address. |
| `source` | string | Server-side source identifier. |
| `ingestip2` | string | Secondary/assigned ingest address. |

`count` equals the number of entries. The CLI retrieves all pages, sorts by
ascending ID, and rejects malformed/repeated cursors, more than 256 pages, and
duplicate IDs. `get` is read-only and may be retried, though state may change.

### Add a destination

```powershell
@($password, 'rtmp://destination.example/live/PRIVATE_KEY') | `
  .\starbonding-cli.exe --username YOUR_ACCOUNT_ID --password-stdin `
  --restream-add --restream-url-stdin
```

Alternatively use `--restream-url URL`. Success requires a valid nonzero ID:

```json
{"schema":"starbonding.restream.v1","success":true,"action":"add","restreamid":42}
```

Do not retry `add` blindly after an ambiguous timeout. The CLI cannot verify
server-side idempotency, so a retry might create a duplicate.

### Activate, deactivate, and delete

```powershell
$password | .\starbonding-cli.exe --username YOUR_ACCOUNT_ID --password-stdin --restream-activate --restream-id 42
$password | .\starbonding-cli.exe --username YOUR_ACCOUNT_ID --password-stdin --restream-deactivate --restream-id 42
$password | .\starbonding-cli.exe --username YOUR_ACCOUNT_ID --password-stdin --restream-delete --restream-id 42
```

Success objects:

```json
{"schema":"starbonding.restream.v1","success":true,"action":"activate","restreamid":42}
{"schema":"starbonding.restream.v1","success":true,"action":"deactivate","restreamid":42}
{"schema":"starbonding.restream.v1","success":true,"action":"delete","restreamid":42}
```

Activate/deactivate set a state and may be retried. Do not retry delete blindly
after an ambiguous timeout because deletion of an absent ID is not guaranteed
to succeed.

### Errors and exit codes

```json
{"schema":"starbonding.restream.v1","success":false,"action":"add","error_code":"SERVER_ERROR","error":"The destination could not be added."}
```

Stable codes are `INVALID_ARGUMENT`, `AUTHENTICATION_FAILED`, `ACCOUNT_ACTIVE`,
`NETWORK_ERROR`, `INVALID_SERVER_RESPONSE`, `PAGINATION_ERROR`, `SERVER_ERROR`,
and `INTERNAL_ERROR`. The descriptive `error` may change; branch on the code.

Restream commands exit `0` only for `success:true`, `1` for authentication,
account, network, server, invalid-response, pagination, or runtime failures,
and `2` for invalid/missing arguments. A recognized restream argument error
also emits its normalized JSON object.

## Public options

| Option | Meaning |
|---|---|
| `--username VALUE` | Account ID; required except for help, version, and self-test. |
| `--help`, `-h` | Print help and product identity. |
| `--version` | Print the installed CLI version and product identity. |
| `--password VALUE` / `--password-stdin` | Supply a password; stdin is preferred. |
| `--remember` / `--forget` | Store/delete a Windows credential. |
| `--authenticate-only` | Validate account and subscription. |
| `--self-test` | Validate the native runtime. |
| `--logout` | Request release of the account lease. |
| `--stop-event NAME` | Gracefully stop when an existing named event is signalled. |
| `--restream-get` | List every destination. |
| `--restream-add` | Add a destination. |
| `--restream-url VALUE` / `--restream-url-stdin` | Supply its URL. |
| `--restream-activate`, `--restream-deactivate`, `--restream-delete` | Change an ID. |
| `--restream-id ID` | Nonzero decimal unsigned 64-bit ID. |

General codes: `0` success; `1` runtime failure; `2` invalid arguments; `5`
authentication failed; `6` subscription ended; `7` another local runtime is
active; `8` account active elsewhere; `9` filename validation failed.

## Streaming events, reconnect, and listener

Stable complete-line machine events are written to **stdout**:

```text
EVENT READY url=rtmp://127.0.0.1:1935/live/starbonding listener=all dual_stack=true
EVENT LINK_SETTLED link=0 heartbeats=3
EVENT STREAM_STARTED generation=1
EVENT PUBLISHER_DISCONNECTED retry=true
```

Reads can split anywhere. Buffer through newline, remove trailing `\r`, and
detect readiness only when a complete line begins `EVENT READY `. Continue
draining stdout and stderr. Human diagnostics and `AUTH_ERROR` use stderr.
Legacy descriptive stdout without `EVENT ` is not machine-readable.

After publisher disconnect, the CLI emits `PUBLISHER_DISCONNECTED`, resets
incomplete media initialization, waits about 500 ms, and accepts another
publisher without re-authentication. The resumed stream emits a new
`STREAM_STARTED generation=N`. Delivery is discontinuous during reconnect.

The RTMP listener binds IPv6 wildcard `::` with IPv6-only disabled, accepting
IPv6 and IPv4 on **all interfaces** on supported Windows. It is not loopback
only. Restrict port 1935 with Windows Firewall. Input is RTMP, not RTMPS.

## OEM integration examples

Always pass arguments without a shell, use `--password-stdin`, concurrently
drain both channels, start a publisher once, and prefer `--stop-event` for
console-independent graceful shutdown.

### Node.js

Node delivers arbitrary chunks, so preserve incomplete lines:

```javascript
import { spawn } from "node:child_process";
const child = spawn(exe, ["--username", account, "--password-stdin",
  "--stop-event", stopEventName],
  { windowsHide: true, shell: false, stdio: ["pipe", "pipe", "pipe"] });
child.stdin.end(password + "\r\n");
let pending = "", publisherStarted = false;
child.stdout.setEncoding("utf8");
child.stdout.on("data", chunk => {
  process.stdout.write(chunk); pending += chunk;
  for (;;) {
    const n = pending.indexOf("\n"); if (n < 0) break;
    let line = pending.slice(0, n); pending = pending.slice(n + 1);
    if (line.endsWith("\r")) line = line.slice(0, -1);
    if (!publisherStarted && line.startsWith("EVENT READY ")) {
      publisherStarted = true; startPublisher();
    }
  }
});
child.stderr.on("data", chunk => process.stderr.write(chunk));
const exitCode = await new Promise((resolve, reject) => {
  child.once("error", reject); child.once("close", resolve);
});
```

Create/signal the named event through a Windows API helper or OEM host.

### Python 3

```python
import signal, subprocess, sys, threading
process = subprocess.Popen(
    [exe, "--username", account, "--password-stdin"],
    stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    text=True, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
process.stdin.write(password + "\n"); process.stdin.close()
def drain_stderr():
    for line in process.stderr: print(line, end="", file=sys.stderr)
stderr_thread = threading.Thread(target=drain_stderr, daemon=False)
stderr_thread.start(); publisher_started = False
try:
    for line in process.stdout:
        print(line, end="")
        complete = line[:-1].removesuffix("\r") if line.endswith("\n") else None
        if complete is not None and not publisher_started and complete.startswith("EVENT READY "):
            publisher_started = True; start_publisher()
finally:
    try:
        if process.poll() is None:
            process.send_signal(signal.CTRL_BREAK_EVENT); process.wait(timeout=15)
    except subprocess.TimeoutExpired:
        process.terminate()
        try: process.wait(timeout=5)
        except subprocess.TimeoutExpired: process.kill(); process.wait()
    finally: stderr_thread.join()
```

Windowless services should use `--stop-event`, not Ctrl+Break.

### C# / .NET

`ReadLineAsync` supplies complete lines:

```csharp
var start = new ProcessStartInfo { FileName = exe, UseShellExecute = false,
  RedirectStandardInput = true, RedirectStandardOutput = true,
  RedirectStandardError = true, CreateNoWindow = true };
start.ArgumentList.Add("--username"); start.ArgumentList.Add(account);
start.ArgumentList.Add("--password-stdin");
using var child = Process.Start(start) ?? throw new InvalidOperationException();
await child.StandardInput.WriteLineAsync(password); child.StandardInput.Close();
bool started = false;
var stderrTask = Task.Run(async () => {
  while (await child.StandardError.ReadLineAsync() is { } line) Console.Error.WriteLine(line);
});
while (await child.StandardOutput.ReadLineAsync() is { } line) {
  Console.WriteLine(line);
  if (!started && line.StartsWith("EVENT READY ", StringComparison.Ordinal)) {
    started = true; StartPublisher();
  }
}
await child.WaitForExitAsync(); await stderrTask;
```

### PowerShell and native Win32

Examples using `ProcessStartInfo.ArgumentList` require **PowerShell 7 or later**.
Use `BeginOutputReadLine`/`BeginErrorReadLine`; their data callbacks supply
complete lines. Signal the named `--stop-event`, retain a bounded wait, then
force cleanup if necessary.

Native hosts should use `CreateProcessW` without a shell, separate restricted
stdin/stdout/stderr handles, correct Windows argument quoting, partial-write
loops, independent line readers, and a unique manual-reset `--stop-event`. A
kill-on-close job object is a final crash-cleanup fallback.

## Security

- Never log passwords, destination URLs, or stream keys.
- Prefer stdin and Windows Credential Manager for secrets.
- Treat restream output as sensitive because it contains destination URLs.
- Firewall the all-interface RTMP listener when remote publishing is unwanted.
- Continuously consume stdout and stderr to prevent pipe deadlock.
