# Latch — deployment and integration handoff

Updated: 26 October 2026. Product: Latch / $LATCH. All product-facing text is English.

## 1. What is being shipped

A browser application with a separate server-side agent service. The interactive Three.js room mounts a shared application inside its monitor. A deployed privacy protocol and transaction execution are not connected. Swap, Send, Bridge and Vault are isolated tabs; MCP and Mesh are marked Coming soon.

| Component | Implemented behavior | Remaining integration |
| --- | --- | --- |
| Swap | Live Raydium amount-specific SOL/USDC quotes, slippage selection, pair reversal, route review | Wallet signing, transaction submission and confirmation; privacy execution |
| LATCH pairs | LATCH can be selected as input or output; the user-provided address is displayed in the site header | Verify the mint and decimals on-chain, configure quoting and confirm an available liquidity route |
| Send | Amount/asset/recipient planning and address-format validation | Balance lookup, transfer construction, signing and submission |
| Bridge | Network and recipient planning | Bridge provider, live fees, settlement and receipt tracking |
| Vault | Browser-generated local notes, commitment verification, export and session-local redemption | Funded pool, proof system, verifier, indexer, relayer and persistent nullifier registry |
| Latch Agent | Server-backed conversation, validated form preparation, follow-ups and explicit review | Transaction execution tools remain unconnected |
| MCP / Mesh | Dedicated Coming soon tabs; conceptual ASCII globe | MCP service and an actual node network |

**Important:** the bridge currently subtracts a fixed 0.50 USDC reference fee. This is not a live bridge quote. Local vault units are test SOL and hold no deposited assets. Deploying the frontend does not activate swaps, transfers, bridging or private custody.

## 2. Local startup

No package installation or application build is required. There is no package.json. Serve the contents of `dist/` over HTTP for visual development:

```sh
python3 -m http.server 8080 --directory dist
```

Open `http://localhost:8080/`. Localhost is a secure context for browser cryptography. Use HTTPS on a public hostname; public plain HTTP does not provide the secure context needed by the vault/clipboard features. Modern browsers with WebGL, ES modules, import maps, BigInt and Web Crypto are required.

Every fresh load starts at the first monitor scene, including URLs with a prior product hash. The Enter Latch control moves to the press-and-hold OS upgrade gate; holding remains required. Product controls can open the workspace during an active visit. MCP and Mesh are selected in the sidebar.

## 3. Static deployment

Upload `dist/` as the web root. Preserve its file structure and query strings. The frontend does not require Node. The live agent additionally uses the Python service described below; its secret and spending ledger are stored outside the web root. There is no server wallet.

Serve JavaScript as JavaScript, CSS as CSS, and keep binary GLB/font/audio files intact. Return 404 for missing assets instead of rewriting every request to index.html. `/work/` is a real documentation directory. Keep index.html and application JS/CSS revalidatable; assets use existing filenames and module query-version suffixes.

The current server deployment uses Nginx, release directories under `/var/www/latch/releases/` and a `current` symlink. The selected web root is `/var/www/latch/current`. Validate Nginx configuration before reloading. Roll back by repointing `current` to the previous release. Keep at least one previous release.

The root `.openai/hosting.json` belongs to the existing Sites publication; it is not required by Nginx and must not be copied into the public web root.

## 4. LATCH configuration

Edit `dist/latch-tokens.js`:

```js
LATCH: { mint: null, decimals: null }
```

The user-provided address displayed in the header is `2oYmpGGMtpU94dp3HFUpJ3WJJSRZngJfkcxYUs8apump`. The copy button in `dist/latch-contract.js` is independent of quote configuration. Verify that address and its decimals on-chain, then configure both fields above. Do not guess decimals or select another token by ticker. A mint alone does not guarantee a Raydium route; confirm liquidity for both directions and intended trade sizes. Missing configuration displays a configuration-pending state and never invents a price.

SOL uses the wrapped SOL routing mint with 9 decimals; USDC uses its configured mainnet mint with 6 decimals. `atomicAmount()` converts input with BigInt and rejects excess fractional precision. The current form validator caps fractional precision at 9 digits even if a future token has more; adjust that validator if supporting such a token. Review supported assets in the agent parser and Send selector when changing token support.

## 5. Quotes and external dependencies

The browser calls Raydium's public Trade API directly:

`GET https://transaction-v1.raydium.io/compute/swap-base-in`

Parameters: inputMint, outputMint, integer atomic amount, slippageBps and txVersion=V0. No wallet address or secret is sent by quote requests. Raydium receives the selected pair, requested amount, slippage and ordinary request metadata such as the client IP. There is no API key. The frontend validates the returned pair, input amount and positive output; errors and missing routes do not fall back to fixed rates.

Input changes are debounced by 450 ms and cancel previous requests. Timeout: 12 seconds. Quote lifetime: 25 seconds. Successful quotes refresh approximately every 20–22 seconds while the swap tab and document are active. Failed or expired quotes require retry. Review is blocked without a fresh quote. Downloaded route plans are not signed transactions or receipts.

Dependencies loaded externally include Three.js 0.170.0 and addons from jsDelivr, Google Fonts, and Raydium quotes. Preserve their access in any CSP/network policy or vendor them before locking down outbound resources. If adding an authenticated API/model provider, keep server keys on a backend; do not place them in dist or browser storage.

Official quote integration reference: https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/api/swap.ts

## 6. Agent contract

`parseCommand(text, previousDraft)` in `dist/latch-intents.js` returns an error code or a draft with kind, parameters and a needs array. English and Russian input recognition exists; the interface and generated replies are English. The local parser is a validation layer. The live agent sends conversation to a server-side model and validates any returned preparation command through this parser before filling a form.

Examples: `Swap 1 SOL to USDC`, `Swap 1 SOL to LATCH`, `Send 0.1 SOL to [public address]`, `Bridge 100 USDC from Ethereum to Solana to [public address]`, `Create a 0.1 SOL note`, `balance`.

The UI uses `window.LatchWorkspace` to select tabs, populate forms, review plans and generate local notes. Each action still requires its relevant explicit review/action button. The balance command reports local notes only, not a wallet or private on-chain balance. Commands containing explicit seed/private-key terminology are refused; this is not a general secret-detection guarantee. Conversation state is in browser memory and shared across tabs. The last 12 messages are sent to the Latch Agent backend for processing; do not describe this as browser-only or private/offline inference. Secrets matching the explicit local filters are not sent; filters are not a general secret-detection guarantee.

## 7. Wallet and vault boundaries

Wallet connection requests a public address from an installed Phantom/Solflare-compatible provider. No transaction signing or broadcasting code is connected.

`dist/sola-core.js` generates a 256-bit random secret with crypto.getRandomValues and computes a SHA-256 commitment. The commitment tag remains `sola-local-v1` for file compatibility. Newly generated notes use `network: solana-local`; imports also accept older solana-devnet note files. Those legacy internal identifiers are not network connectivity or current branding.

Local notes are not zero-knowledge proofs and carry no deposited assets. There is no pool program, prover, custody account or durable nullifier set. The in-memory note/spent registry resets on reload. Importing a note verifies its JSON/commitment and can mark it redeemed for the current session; it does not transfer assets or import a persistent balance. Secrets are masked until revealed and leave the browser only through explicit copy/export. No security audit is claimed.

## 8. Code map

- main.js / os.js / crt-screen.js: scene, monitor transitions and mount lifecycle. Preserve gate/camera events when integrating the app.
- sola-products.js: shared application shell, forms, review, wallet address and local note UI.
- latch-tokens.js: mint configuration, exact input units and live quote transport.
- latch-contract.js: user-provided contract address and header copy button; it does not configure trading.
- latch-intents.js / latch-agent.js: command planner and shared panel.
- latch-avatar.js / latch-processes.js / latch-ascii.js: decorative ASCII avatar, separate process diagrams and backdrop.
- latch-mesh.js: conceptual network globe and Mesh tab.
- sola-monochrome.css / latch-agent.css: final workspace styling overrides.
- assets/: scene models, textures, fonts, audio and brand files. Do not rename dependency paths casually.
- work/index.html: public technical documentation.

Animation states and Mesh nodes are visualizations, not transaction progress or live infrastructure telemetry. Social icons are intentionally non-navigating until official GitHub/X URLs are supplied. The production symbol is assets/brand/latch-symbol.svg: a connected keyhole-shaped agent, white body and cyan #00D9FF eyes.

## 9. Checks and release acceptance

Use a current Node runtime (tested with the bundled current runtime):

```sh
node test-sola.mjs
node test-latch-intents.mjs
node --check dist/sola-products.js
node --check dist/latch-agent.js
node --check dist/latch-tokens.js
```

Browser release checks: load `/` and `/#swap`; verify scene/monitor transitions, all six tabs, live SOL/USDC quotes and pair reversal, missing LATCH configuration, expired/failed quote behavior, local note export/import and masked secrets, English agent replies, distinct process animations, social icon placeholders and `/work/`. Check narrow/mobile layouts on the deployment host. A successful quote is not an end-to-end swap test.

Before enabling real asset movement, developers must implement and test transaction review, simulation, user signing, submission, confirmation, error recovery, provider-specific fees and privacy guarantees against actual receipts. Select and verify the privacy architecture before making protocol/security claims.

## 10. Production operations

Primary hostname: https://latchprotocol.fun/. The root-domain A record points to the deployment server. The www hostname currently points elsewhere; it is not part of this deployment or its certificate. Set its DNS deliberately before adding it to Nginx/certificates.

Nginx site: /etc/nginx/sites-available/latch, enabled through /etc/nginx/sites-enabled/latch. Release root: /var/www/latch/current. The domain uses Certbot webroot validation through /var/www/letsencrypt and the existing certbot.timer. Certificate files are under /etc/letsencrypt/live/latchprotocol.fun/. Renewal deploy hook validates and reloads Nginx. Other server virtual hosts remain separate.

Read-only checks: nginx -t; systemctl status nginx; systemctl list-timers certbot.timer. Test renewal with certbot renew --cert-name latchprotocol.fun --dry-run. Never copy certificate private keys or server credentials into the site/source repository.

## 11. Live agent service

Python standard-library service: backend/agent_server.py, listening only on 127.0.0.1:8787. Unit: /etc/systemd/system/latch-agent.service; code: /opt/latch-agent/agent_server.py; user: latch-agent. Nginx proxies POST /api/agent and GET /api/health. The frontend contains no API credential.

The service reads REQUESTY_API_KEY from /etc/latch/agent.env (root-only, not shipped in source). It calls the configured OpenAI-compatible routing endpoint server-side. Model identity stays out of the public UI. Do not change the selected model without reviewing budget pricing; the current deployment rejects unreviewed model changes.

A persistent SQLite ledger at /var/lib/latch-agent/budget.sqlite3 enforces an overall USD 20 application budget using conservative atomic per-request reservations, a USD 0.05 maximum reservation per call, two simultaneous requests and 8 requests per IP per minute. Unknown/failed billing retains its reservation. The ledger survives service restarts. Do not delete/reset it, or run additional replicas against separate ledgers, to preserve the total cap. This does not top up the provider account or cap spending from other uses of the same key. Upstream billing remains subject to provider prices and account controls.

The backend accepts only the production Origin, bounded JSON histories, supported roles and validated preparation commands. It cannot sign, submit transactions or spend assets. Response texts are model-generated; route/form validation and explicit review remain required. No prompts, conversation bodies or key values are written to application logs. Conversation still leaves the browser for inference.

Operations: systemctl status latch-agent; journalctl -u latch-agent; curl https://latchprotocol.fun/api/health. Tests: python3 -m unittest discover -s backend -p 'test_*.py'. To disable inference while preserving the static site, stop latch-agent. Configure a real provider budget cap in the provider console for an additional account-level bound. Never commit the environment file or ledger.

### Agent deployment steps

1. Create the unprivileged latch-agent system user and /opt/latch-agent with world-readable traversal (0755).
2. Install backend/agent_server.py to /opt/latch-agent/agent_server.py, owned by root, mode 0644.
3. Create /etc/latch/agent.env from deploy/agent.env.example, root-owned mode 0600, and provide the key securely.
4. Install deploy/latch-agent.service as /etc/systemd/system/latch-agent.service. Run systemctl daemon-reload and systemctl enable --now latch-agent. systemd owns the persistent StateDirectory.
5. Merge the API locations from deploy/latch.conf into the intended Nginx virtual host, run nginx -t, then reload Nginx.
6. Check GET /api/health and a short POST /api/agent from the allowed production Origin. Do not reset the ledger while testing or redeploying.

The backend Origin is fixed to https://latchprotocol.fun. If moving the application to another hostname or developing with a local reverse proxy, review that allowlist and the proxy before testing inference. A plain local static server intentionally has no live inference API.
