# SDK bot fleet example

This is a reference implementation of a long-lived multi-account agent fleet built entirely on `@glyphteck/veyl`. It uses no Firebase Admin connection, privileged chat path, server action queue, or cloud secret store. The Veyl-managed runtime under `bots` consumes this policy with its own local lineup and operator controls.

The secret-free version-2 manifest stores account indices, usernames, networks, roles, and enabled state. One owner-only root at `$VEYL_HOME/fleets/<name>.seed` derives isolated account keys, vault keys, and Veyl master seeds for monotonic account indices. Per-account private material is never stored in the manifest.

## Roles

- `read` subscribes through the public event API and advances directly from the listener's newest decrypted activity before enabled effects run, including a bot's own confirmed reply. The canonical runtime keeps that chat's ordinary encrypted live room for five idle minutes, flushes an already-connected relay frontier before emitting the event, lets a cold room publish its frontier in the initial socket snapshot without waiting on the handshake, coalesces the durable write, and never acquires a lease for checkpointed startup history. The event checkpoint remains post-effect for safe replay.
- `echo` mirrors text, encrypted attachments, and payment requests with deterministic action ids. Group members derive exactly one source-specific eligible fleet echo from the encrypted roster for each source message; every other echo remains silent and only reads. Selection distributes independently across messages, so the same bot may legitimately win consecutive messages.
- `faucet` pays valid requests through the public wallet API and journals caller operation ids.
- `traffic` is an eligibility marker for an external local operator policy; it performs no work by itself.
- `live` holds the account's normal encrypted live-room connection in each chat without advancing read state or sending messages. The concrete host supplies the same realm-matched portable live transport used by graphical clients; startup rejects this role when that client port is absent.
- `typing` requires `live` and continually renews one stable composition through the same encrypted live state until the policy stops.
- `voice` starts muted and joins the newest active, foreign-occupied call among the account's watched chats. It uses `client.chat.watch(onChats, { count: 500 })`, `client.calls.observe(chatId, onAvailable)`, and strict `joinExisting(chatId, callId)`; it never creates a call or inherits read/echo behavior. The host must supply real `calls.media` and ephemeral `calls.mls` ports. One shared owner admits the new destination before leaving an old call, supports cancellation, and leaves when no other account remains. A failed unchanged room is not retried on every heartbeat.

Only response-capable fleet peers are loop-suppressed. Incoming transaction events and staggered fallback loops call the public wallet claim method. Startup replays a bounded visible window; an event checkpoint suppresses completed source messages, while the action journal suppresses completed effects and stops ambiguous payments for explicit reconciliation.

The fleet owner keeps one unlocked public SDK client per account. Root-derived fleets supply an ephemeral account master seed directly into the normal vault session flow, avoiding a redundant password KDF while preserving the server-verified vault signature and encrypted registry checks. Account readiness waits for auth, vault proof, and chat-list ingress only; wallet boot, transaction history, and per-chat replay hydrate in the background. `owner.reload()` imports a fresh policy and reconciles the manifest without reopening unchanged clients.

## Try a disposable fleet

1. Create a seed with `createFleetSeed` and an empty version-2 manifest with `saveFleetManifest`.
2. Open the owner and call `owner.provision(...)` for disposable REGTEST accounts.
3. Assign `faucet` only to an explicitly funded account whose operator accepts valid peer requests.
4. Build the public package with `bun --cwd sdk/javascript run build`.
5. Start this explicit entrypoint with `VEYL_FLEET_MANIFEST=/absolute/path/to/manifest.json bun sdk/javascript/examples/bot-fleet/index.js`.

The bounded live harness is opt-in because it creates and deletes real REGTEST accounts:

```bash
bun check:bot-fleet-live
```

It uses a separate random local home, proves creation/recovery, text/request/attachment echo, retention, immediate reads with post-policy checkpoints, faucet behavior, transfer claim, restart, final balance reconciliation, and normal SDK account deletion. An interrupted run prints the disposable manifest path and can resume with `VEYL_LIVE_BOT_FLEET_MANIFEST=/absolute/path/to/manifest.json bun check:bot-fleet-live`.

Reserved usernames require an owner-only namespace key. Provisioning signs a short-lived claim bound to the exact derived machine credential; the key is never accepted through argv, environment variables, or the manifest.

The local fleet PID lock and control socket are user-only. A second owner fails and never stops or replaces a healthy process.
