sdk version: 1.1.0

# @glyphteck/veyl

Use a Veyl account from Node.js or the shell. The package provides:

- account, profile, and peer management
- end-to-end encrypted notes, direct chats, groups, files, reactions, and payment requests
- self-custodial veyl wallets for peer payments, Lightning, and Bitcoin withdrawals
- a JavaScript API, JSON CLI, and live events for agents and automations
- explicit chat reads and receipts, encrypted chat presence, writing state, and headless audio, camera and screen streaming through the same account runtime
- nearby account discovery through the shared account owner and a bundled macos bluetooth helper

Requires Node.js 22 or newer.

## connect your agent

tell your agent: **connect to veyl.chat and message me at @yourusername.** the agent can read [the setup contract](https://veyl.chat/llms.txt), install this package, create or restore its own account, and connect its message listener to its existing harness. you do not need to copy a code example or configure the documentation mcp.

the agent needs command execution, private durable storage, and a host that can keep it running. see [agent operation](/sdk/agents#setup) for the setup and [the packaged connector](/sdk/connector) for supported model hosts and the shared harness contract. verify a human message and agent reply before treating the connection as ready.

```bash
veyl agent connect @yourusername --harness opencode
```

the command creates or restores one agent account, retains its live client, and routes incoming messages and payments to the existing model harness. use `--harness codex` for that host. account names default to a neutral unique agent name; model-provider and wallet-provider names are not account identity.

source and installed runtimes both use sdk-owned wasm and media assets. `bun dev bot`, `bun dev cli`, and aggregate development prepare those assets before starting their children and rebuild them before each bot child restart. before other direct source runners, run `bun scripts/build-sdk-assets.mjs`; `bun run --cwd sdk/javascript build` includes the same asset preparation.

private cache decryption, checkpoint parsing, source hashing and retained search indexes run in one sdk-owned node worker per account execution family. repeated consumers of a scope share its physical cache and index; closing one consumer preserves its siblings, while destruction retires all of them before removing storage. the caller exchanges bounded frames and presentation results. the package build includes the fixed `cache-worker-thread.js` asset, and temporary sessions keep their cache bodies inside that worker's memory rather than writing to the sdk home. [the platform owner](src/runtime/cacheexecution.js) selects the worker; [the shared actor](../../core/cache/execution.js) owns the cache and retrieval rules.

the shared `openAccount()` composition exposes `account.search.capabilities({ language: 'en' })` for read-only local model readiness. it returns embedding status/reason and compatible-space metadata with `localOnly: true` without reading chat inventory or messages, generating text or running embeddings. the language tag is required. the current node worker reports `unsupported-platform`; a native platform supplies its own admitted local model port. [private search](../../core/search/README.md) owns this interface and its unlocked account/cache lifetime.

each persistent account has one physical worker owner across node workers and processes. every scope acquires account admission before its directory claim or any directory creation; all scopes in the same worker share that account claim until the last scope finishes draining and releases it. an independent runtime receives `cache/owned`, including when it requests a different scope of that account. final claim release is a barrier for new admission. wallet orphan removal also acquires its target directory claim; full account deletion acquires account admission before taking the directory inventory, so an admitted scope without a directory cannot escape deletion. the platform can reclaim its own claims only after verifying that exact worker has terminated, and token retirement prevents delayed cleanup from touching a successor. claims contain process and worker ownership metadata; canonical scope names remain in authenticated, encrypted cache metadata. [the directory owner](src/runtime/cache-claims.js) uses [the shared private claim authority](src/private-claim.js).

## privacy boundary

an agent can read messages addressed to it. whether it stores them or forwards them to a model provider depends on its operator. veyl’s deletion settings do not erase copies held by the agent or its providers.

on macos, an open unlocked sdk account advertises and scans for nearby accounts by default. the bounded bluetooth exchange sends signed public identity in plaintext; it never sends private keys, messages or payments. `settings.update({ hideFromNearbyUsers: true })` stops publication while keeping discovery available. this is separate from public internet presence. other node platforms have no radio adapter and contribute no nearby results. the macos helper starts only when the account admits radio work and exits on lock or close; it belongs to that runtime, with no separate discovery daemon. source runners must prepare the bundled helper through the existing sdk asset build before opening a macos runtime.

account credentials and private keys reside on the machine running the sdk, which may be a server operated by someone else. the documentation mcp is read-only; the sdk and cli are the tools that access accounts, chats, and wallets.

## install

```bash
bun add @glyphteck/veyl
# or: npm install @glyphteck/veyl
```

## quick start

```js
import { open } from '@glyphteck/veyl';

const veyl = await open({ profile: 'runner', network: 'REGTEST' });
const accountKey = await veyl.account.create({ username: 'runner' });
const vaultKey = await veyl.vault.create();

await veyl.chat.send('@alice', 'hello');
console.log(await veyl.wallet.balance());
await veyl.close();
```

`accountKey` authenticates the account. `vaultKey` unlocks its chat and wallet. By default, both are saved in `~/.veyl/profiles/runner.json` with owner-only permissions so later runs can call `account.login()` and `vault.unlock()` without passing them again.

a vault created in the apps can instead use one compatible account passkey. after passkey login, `vault.unlock()` uses that exact credential through the system browser, without saving its prf secret in the sdk profile. export and verification request fresh passkey authority. `vault.changeKey({ newKey })` adds a password and removes the current passkey unlock path; the new password follows the ordinary sdk storage policy. additional account passkeys are disabled until that conversion. passwordless creation is exposed by the shared `openAccount().createPasskeyVault()` owner used by the apps; high-level sdk creation remains the explicit password/key flow.

To store the keys in your own secret manager, pass `saveKey: false` to both creation calls and provide them later through `open({ profile: 'runner', accountKey, vaultKey })`, `VEYL_ACCOUNT_KEY`, and `VEYL_VAULT_KEY`. Never log or commit either key.

create and unlock the vault normally, then call `veyl.wallet.create()` for a new private veyl wallet. `veyl.wallet.import(recoveryPhrase)` adds one from a privately supplied 12-word english phrase. additions stay in the encrypted vault; their identifiers are never published in the account profile or reserved against other accounts. the original wallet remains the account's single public receiving wallet. payments to your username always use that primary wallet, regardless of your active selection.

read your private inventory with `veyl.wallet.list()` and switch with `veyl.wallet.select(walletId)`. each row identifies the pinned primary with `primary: true`; secondary rows follow their most recent selection. the current api keeps the initial primary and does not expose rotation. only the remembered active wallet starts at unlock; other wallets show cached amounts and receiving addresses. account payment links and veyl qr requests target the primary; direct bitcoin addresses and lightning invoices can receive into the selected secondary. only empty secondary wallets can be burned, after fresh bitcoin and token checks on both networks; graphical clients require confirmation; account deletion retires the primary. a chat payment can privately teach its addressed recipient that the source wallet belongs to you, using a wallet-control signature. the recipient still sends to your public primary by default. private wallets are not anonymous: recipients, providers and public bitcoin activity can still reveal links. see the [vault api](/sdk/api#vault) for creation, import, deletion and per-wallet export.

accounts and firestore share one company-wide backend across development, testing and production. use `REGTEST` for development and disposable accounts; it changes the wallet network, not the account, vault or database. `MAINNET` uses real funds. Creating an account accepts Veyl's [Terms](https://veyl.chat/terms#terms), which include the community rules.

vault unlock admits the account session without waiting for wallet history hydration. the existing history owner publishes local rows when ready; payment and movement operations still wait for their own history and wallet readiness.

## temporary sessions

temporary accounts support headless chat, encrypted files, calls and one fresh wallet through the same account owners. browser, desktop and ios creation flows remain unavailable.

choose the lifetime before authentication in a long-running javascript client:

```js
const veyl = await open({ lifetime: 'temporary', network: 'REGTEST' });
try {
  await veyl.account.create({ confirm: true });
  const sent = await veyl.chat.sendAttachment('@recipient', {
    type: 'file', name: 'documents.txt', mimeType: 'text/plain', data: bytes,
  });
  if (sent.delivered !== true) throw new Error('recipient delivery remains unconfirmed');
  const end = await veyl.account.logout();
  // inspect end.remoteEnded for confirmation of server retirement.
} finally {
  await veyl.close();
}
```

this session has no account key, vault password or recovery phrase. lock, logout, close and the server's absolute 24-hour expiry permanently remove access, including to its wallet. withdraw funds before ending; pending or later payments may become inaccessible. no profile or private cache is written to the sdk home. backgrounding alone does not end it.

temporary accounts can reserve 16 encrypted files totaling 64 mib, retained for at most seven days even in saved messages. downloads have a 256 mib lifetime response budget; authenticated callables have a 2,000-request budget and call tickets have a 16-ticket budget. ending remains available after exhaustion. check `sent.delivered === true` before treating a send followed by end as a completed handoff. a message can remain committed locally while interrupted introduction delivery rejects; `error.committed`, `cid`, `msgId` and `epochId` identify that append for `chat.retry(peer, cid)` while the session remains active. retry authenticates its existing ciphertext without another file upload. confirmed files remain available to recipients after the sender ends; delivery does not mean the recipient read them. offline end still discards local keys but reports `remoteEnded: false` until remote retirement is confirmed or the original deadline expires.

this does not hide network metadata or erase recipient copies, source documents or operating-system traces. see the [account api](/sdk/api#account) for the lifetime contract. the shell cli uses durable profiles; temporary accounts require one javascript process.

## CLI

The CLI prints JSON, which makes it suitable for shell scripts and agents.

```bash
npm exec --package=@glyphteck/veyl -- veyl --profile runner --network REGTEST account create @runner
npm exec --package=@glyphteck/veyl -- veyl --profile runner --network REGTEST vault create
npm exec --package=@glyphteck/veyl -- veyl --profile runner --network REGTEST chat send @alice "hello"
npm exec --package=@glyphteck/veyl -- veyl --profile runner --network REGTEST wallet balance
```

run `npm exec --package=@glyphteck/veyl -- veyl help` for the full command list. the explicit package selector avoids picking an unrelated or older `veyl` executable from the shell path. long-running agents should keep one javascript client or persistent cli session open instead of starting a new wallet and chat session for every action.

select the account profile explicitly on every client or command. an active profile from an earlier run does not choose the account for an agent. message reads and listener message events acknowledge messages by default; use `read: false` or the cli's `--no-read` for inspection without receipts. reading does not automatically publish presence or writing, or accept an incoming chat request. payments require `wallet.preparePayment(intent)` followed by `wallet.sendPayment(review)` with the unchanged review.

confirmed message `id` values include their epoch and select that exact source across history. `epochId` identifies the authenticated epoch; `cid` remains the composition id used for local retry and cancellation. bare message selectors address the current chat epoch only. use the returned `id` when selecting historical messages.

## documentation

- [JavaScript API](/sdk/api)
- [CLI reference](/sdk/cli)
- [agent operation](/sdk/agents)
- [headless calls and streaming](/sdk/calls)
- [persistent agent connector](/sdk/connector)
- [MCP documentation discovery](/sdk/discovery)

## license

This package is proprietary. The unmodified SDK may be used with Veyl; see the [license](/sdk/license) for terms.
