# Development runbook

## Resume safely

Read README.md, AGENTS.md, docs/STATUS.md and docs/DECISIONS.md. This experiment uses Devnet only. No mainnet deploy or real-value transaction is authorized. The website and Solana program are separate deployments; editing one does not automatically update the other.

The source checkout is `/workspace/sites/solana-discovery-lab`. The Site project identity is in `.openai/hosting.json`. Use the existing Sites project and its repository if this local checkout disappears. Source is durable in the Sites Git repository. Secret key material is deliberately absent from that repository.

## Check tools before compiling or deploying

Check the current environment before every resumed Rust build/test or Solana CLI deployment. Project files and saved executables may remain when the separately installed tools are missing; an earlier successful build does not establish present compiler availability.

1. Locate `cargo`, `rustc`, `rustup`, `solana` and `cargo-build-sbf` as required for the intended operation, using `command -v` and the recorded installation paths below. Run their `--version` commands and verify the selected Rust toolchain with `rustup show active-toolchain`.
2. If the expected binaries exist but are outside PATH, use the documented project-specific PATH. If binaries or the selected platform toolchain are absent, reinstall the pinned versions using the instructions below. Verify compatibility rather than silently substituting a newer version. Keep this installation outside the repository and the managed Codex runtime.
3. Repeat the executable/version checks after repair. Only then run the authorized build, tests or deployment. Redeploying an unchanged saved executable requires the deployment CLI but does not require recompilation. Website-only work does not require the Rust toolchain.

**Shared installation restored, 2026-09-07:** Rust/Cargo and Solana tools are installed outside `/workspace` and available by name in fresh login and interactive Bash shells. The previous installation was missing; its disappearance was not proven to be an environment replacement. These installations can be used by other chats sharing the same filesystem and root account, but are not guaranteed to survive a replacement environment or appear on a separate machine. The project source and saved program artifacts remain in Git.

## Shared toolchain locations

| Component | Version / path |
|---|---|
| Node | 24.19.0 |
| npm | 11.9.0 |
| General Rust and Cargo | 1.98.1, commands in `/root/.cargo/bin/`; stable toolchain in `/root/.rustup/toolchains/` |
| rustup | 1.29.1, `/root/.cargo/bin/rustup` |
| Rust components | Compiler, Cargo, standard library, docs, rustfmt, Clippy, rust-src and rust-analyzer |
| Solana CLI | 4.2.2, `/usr/local/lib/solana/releases/4.2.2/solana-release/bin/solana` |
| Active Solana release | `/usr/local/lib/solana/active_release` points to the versioned release above |
| cargo-build-sbf | 4.1.0, in the active Solana `bin/`; also callable as `cargo build-sbf` |
| W VIBE platform tools | v1.57, `/usr/local/lib/solana/platform-tools/v1.57/`; Rust 1.95.0-dev, ae660768a, 2026-08-17 |
| Builder default platform tools | v1.54, `/usr/local/lib/solana/platform-tools/v1.54/`; W VIBE explicitly selects v1.57 |
| Platform cache links | `/root/.cache/solana/v1.54/platform-tools` and `/root/.cache/solana/v1.57/platform-tools` point to their versioned installations |
| Rust dependency | solana-program exactly 3.0.0; commit program/Cargo.lock |
| JS chain SDK | @solana/web3.js 1.98.4; use package-lock.json |

`/root/.local/share/rust-solana/env.sh` adds `/root/.cargo/bin` and `/usr/local/lib/solana/active_release/bin` to PATH without replacing the managed runtime or existing PATH. It is sourced at the beginning of `/root/.bashrc` (before the noninteractive return) and `/root/.profile`. Existing shell startup content, including Go's PATH entry, is retained; original files are backed up under `/root/.local/share/rust-solana/startup-backups-2026-09-07/`.

An already-running shell or a noninteractive shell that does not load startup files can enable the tools explicitly:

```bash
. /root/.local/share/rust-solana/env.sh
cargo --version
rustc --version
solana --version
cargo build-sbf --version
```

`/usr/local/bin` is not writable in this environment; `/root/.local/bin` is writable. They are different directories. Go remains at `/usr/local/go/bin/go`. No managed Codex runtime directory is used for these installations. General `cargo` uses stable Rust; W VIBE uses the separate pinned SBF compiler. A convenience `solana-v1.57` rustup link selects that compiler explicitly without changing the global default. The SBF builder also manages its own versioned toolchain entry.

Anchor, Surfpool, a private cluster and MagicBlock are not used by this workflow. The CLI release includes validator tools, but no validator is started by installation.

## Reinstall compiler if needed

Use official release artifacts and verify downloads before execution. Keep installations outside `/workspace` and the managed Codex runtime:

- [Agave v4.2.2](https://github.com/anza-xyz/agave/releases/tag/v4.2.2), asset `solana-release-x86_64-unknown-linux-gnu.tar.bz2`.
- [Platform tools v1.57](https://github.com/anza-xyz/platform-tools/releases/tag/v1.57), asset `platform-tools-linux-x86_64.tar.bz2`.
- [Platform tools v1.54](https://github.com/anza-xyz/platform-tools/releases/tag/v1.54), same asset name, for the builder's default version.
- [Rustup installation](https://rust-lang.github.io/rustup/installation/index.html): use the official Linux x86-64 `rustup-init` binary and its `.sha256` from `https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/`.

Download files can be staged under `/root/.cache/toolchain-downloads/`. Compare GitHub release archives with the release API's SHA-256 asset digest and rustup with its official checksum. Install rustup using `--no-modify-path --profile default --default-toolchain 1.98.1 --component rust-src --component rust-analyzer -y` to reproduce the verified general compiler version; the current installation is the stable channel at that version. Extract archives with `tar --no-same-owner`: the CLI under `/usr/local/lib/solana/releases/4.2.2/`, and each platform archive under its versioned platform-tools directory. Recreate the active-release and cache links listed above, then configure and source the shell environment file.

```bash
rustup toolchain link solana-v1.57 /usr/local/lib/solana/platform-tools/v1.57/rust
rustup show active-toolchain
rustup run solana-v1.57 rustc --version
```

Keep the general compiler as the default rather than globally selecting the Solana compiler. A missing binary or network failure requires diagnosis; do not assume that a reinstall succeeded. Network/API access can differ between a connector, workspace shell, installer and hosted Worker.

## Compile the program

From the checkout, with the shared environment loaded:

```bash
cargo build-sbf --tools-version v1.57 --skip-tools-install --manifest-path program/Cargo.toml -- --locked
```

Output: `program/target/deploy/discovery_registry.so`. Tool-generated keypairs are ignored. Macro warnings for custom-heap/custom-panic did not prevent compilation. Never treat successful compilation as deployment.

The 2026-09-07 installation check ran all eight existing Rust tests successfully and rebuilt W VIBE using the pinned SBF compiler in a separate cache directory. The resulting 33,104-byte executable exactly matched the saved v0.3.0 SHA-256. A separate dependency-free SBF program also built successfully using the builder's default v1.54 compiler. Versions, installation paths and verified archive hashes are retained in `evidence/toolchain-installation.json` and `/root/.local/share/rust-solana/installation.json`. No program upgrade, chain transaction or Site publication was performed for this installation.

## Test keys and funding

Local `.secrets/` contains deployer, Alice, Bob, attacker, program and buffer JSON keypairs, plus `decoder.txt` (base64 32-byte AES key). Directory mode 700, files mode 600. They are disposable **Devnet-only** keys. Never print, commit, download into the browser, or use them for Mainnet.

The site has `DEVNET_SIGNERS_JSON` (deployer/alice/bob/attacker secret arrays) and `DECODER_KEY_BASE64` stored as server secrets. Sites returns these masked; do not assume a new chat can read them back. Runtime actions continue to work after a new conversation. If local secrets are lost, a new local test setup may require new test wallets, funding, program and decoder; old ciphertext cannot be decoded without the old key. Separate production key backup and rotation are not implemented.

Public deployer: `3yh5HvdW6t6t1bR3THpFgQYKM7eqdGr4t5YTWbuh2w5J`.

Alchemy can request test SOL, but ordinary HTTPS RPC does not require the connector. Confirm funding by balance and parsed receipt; a finalized memo alone does not prove a transfer. Respect faucet amount/rate limits.

No repeated automated faucet requests are part of the site. Respect rate limits. Alice and Bob are funded for buyer-paid test sales. Some lab actions still use the issuer as fee payer while owners sign. Funding amounts and actual receipts are recorded in the fixture evidence; do not refill wallets unnecessarily.

## Deploy / verify / exercise

```bash
python scripts/solana/upgrade-issuance.py
node scripts/solana/verify-deployment.mjs
node scripts/solana/lifecycle.mjs
node scripts/solana/verify-site-actions.mjs
# Resume a partially completed saved run without repeating recorded transactions:
node scripts/solana/verify-site-actions.mjs --resume
```

The issuance upgrade script validates the Devnet genesis and uses explicit program/buffer keypairs, RPC transport, and confirmed commitment. It captures CLI output to avoid accidental recovery-seed printing. It records the receipt and binary hash. Deployment and recovery scripts use SOLANA_BIN when set, otherwise `solana` from PATH; they do not depend on the removed workspace installation.

Verification compares on-chain executable bytes with the build and confirms the deployment receipt. The lifecycle script submits four successful transactions; rejection cases are simulations. The --resume form is for an incomplete run only; do not run it after a completed run. The site-service verification exercises the shared server code, exact error codes, missing claimant/owner signatures, and Mainnet endpoint refusal. It creates test records and consumes only test SOL. Do not rerun tests gratuitously: records cannot currently be closed.

If a submit returns a signature but confirmation times out, inspect that existing signature before retrying. The UI saves pending signature/address in localStorage and provides a confirmation button. It waits for confirmation before enabling the next write. The browser is not the owner-of-record database; Solana is.

## Website runtime

`app/api/lab/route.ts` reads server bindings from `cloudflare:workers` and calls `lib/server-lab.mjs`. The shared chain code is `lib/chain.mjs`. Only enumerated demo actions are exposed, only records issued by this lab can be mutated, and POST requires the same origin and JSON. The Site is public. Disposable server-wallet actions belong to the Devnet playground at /lab. Same-origin checks do not identify individual users. Production authorization and rate limits remain open; consumer Phantom onboarding currently reads a public address/test balance without signing.

Bindings:

| Name | Secret? | Purpose |
|---|---|---|
| SOLANA_RPC_URL | If authenticated, yes | Defaults to https://api.devnet.solana.com |
| DEVNET_SIGNERS_JSON | Yes | Disposable test signers |
| DECODER_KEY_BASE64 | Yes | AES-GCM seed encryption / decryption |

Only Devnet public RPC or solana-devnet.g.alchemy.com hosts are accepted. Every signed write verifies the genesis hash `EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG`. The site's hosted runtime does not inherit Alchemy connector authentication. To switch providers, set a server-secret RPC URL from an Alchemy Solana Devnet app, then redeploy. The hosted Site uses a server-side Alchemy URL secret; its actual-chain verification is in evidence/published.json. The connector returns masked keys.

The consumer routes live in components/vibe-page.tsx. GET /api/vibe/network verifies Devnet and returns completed cached slot/program status with request-owned fetches; GET /api/vibe/wallet validates the address and reads test balance after explicit Phantom connection. Network, wallet and gallery reads never sign or write. The separate Chase endpoint can co-sign and submit an explicitly approved free claim. The Flex, Moves and Moments read `/api/vibe/data`; `lib/vibe-gallery.mjs` and `lib/vibe-dataset.json` define validated reads, publication and frozen fixture policy. `lib/vibe-issuance.mjs` authenticates bound recipes and renders type-specific scheduled Looks; `lib/vibe-records-v2.mjs` parses accounts and executes unsigned read-only program quotes. Old image studies only support explicitly illustrative concept screens. Read docs/HOMEPAGE-V1.md for exact fields and timing.

Use `node scripts/verify-mint-v3.mjs` for the current 100-record integration and signed authorization simulations. JavaScript tests cover recipe authentication, calendar boundaries and game dispatch; Rust tests cover the corresponding program rules. Earlier gallery/browser evidence describes its original generation only.

`node scripts/solana/readiness.mjs --save` compares source/artifact/deployed bytes and authenticates a legacy identity. `seed-gallery-v2.mjs --write` creates the current 100-record generation once; do not rerun completed seeding. `--resume` verifies saved receipts. The private write-ahead journal and original record keys support explicit recovery; see [BOOTSTRAP.md](BOOTSTRAP.md). Older seed scripts and evidence describe narrower legacy experiments.

## Docs, build, and publish

The documentation page embeds the committed Markdown, generated by `node scripts/build-docs.mjs`. This also copies non-secret evidence into public downloads. Regenerate it after changing docs or evidence. No filesystem reads are required in the hosted Worker.

Use the Sites building/hosting skills for dependency installation, build, exact-source Git push, packaging, and requested deployment to the current audience. W VIBE is public; preserve the requested audience. Do not create another Site. Keep the existing starter, lockfile, `sites()` Vite plugin, Worker entrypoint and hosting manifest. Runtime environment changes require redeployment to apply. Saving documentation does not itself request deployment.

The shared client defaults used by the lab/write paths back off on HTTP 429, honor numeric Retry-After within a 12–30 second bound, and retry at most twice. The consumer gallery and network read routes override this: bounded request-owned abort signals, shorter per-RPC deadlines, and one read-only retry for transient network/HTTP failures; cancellation interrupts backoff and the overall request deadline remains enforced. A request timeout or other failure remains visible. The browser keeps a returned pending receipt for inspection. A lost response before the server returns a receipt remains an unresolved production idempotency requirement.

Basic source check: `node_modules/.bin/tsc --noEmit`. Use the Sites build helper, focused mint/issuance/receipt tests, and the current integration scripts below. Generic starter tests and historical browser replay are not current blockchain acceptance evidence.

## References

- [Solana clusters](https://solana.com/docs/references/clusters)
- [Rust programs](https://solana.com/docs/programs/rust)
- [Program deployment](https://solana.com/docs/programs/deploying)
- [Accounts](https://solana.com/docs/core/accounts)
- [Transactions](https://solana.com/docs/core/transactions)
- [Alchemy Solana quickstart](https://www.alchemy.com/docs/reference/solana-api-quickstart)


## Published-site verification

`scripts/verify-published.mjs` checks hosted Alchemy Devnet connectivity, reads/decrypts an existing record, submits one owner-signed evolution, confirms its receipt, verifies invariants and simulates rejected actions. It writes non-secret results to evidence/published.json. Supply its URL/auth settings only through process environment, never source. Use the current Sites skill's testing rules before any hosted verification. A documentation update is not a reason to submit a new test transaction.


The test advances a Devnet record; do not repeat it unnecessarily. If submission/confirmation fails, inspect the saved signature before rerunning. The current script does not automatically resume an interrupted submitted transaction.

The verified executable and its manifest are now preserved in program/artifacts/. For reset recovery and iterative changes, read docs/BOOTSTRAP.md. No automated deployment-on-reset hook was introduced.

## Discovery verification

The original published wallet-signed claim succeeded and is retained in `evidence/chase-claim.json`. It refers to the original V2 sample. Its owner is carried into the refreshed counterpart; its old receipt is not relabelled as a V3 game transaction. Current verification uses signed simulations and the hosted three-profile game check.

## Current minting verification

`node scripts/solana/verify-deployment.mjs` compares deployed bytes and receipt. `node scripts/solana/seed-gallery-v3.mjs --resume` resolves the checkpointed batch; `--restore` is reserved for verified missing-state recovery. `node scripts/verify-mint-v3.mjs` performs actual Devnet reads and signed simulations without broadcasts. After publishing, `node scripts/verify-chase-hosted.mjs` verifies every game profile through the live Site. `tests/mint-v3.test.mjs` checks the authored-input contract and tamper rejection.

Do not rerun the original ownership-changing test as a new claim. Its confirmed receipt is in `evidence/chase-claim.json` and refers to the archived original generation. A new generation invalidates prior play tickets and must never redirect an unresolved transaction to another asset.


The user explicitly ruled out the internal preview browser. Publish coherent checkpoints and verify the live Site APIs over HTTP against actual Devnet; do not retry cloud-browser navigation to a published Site.

## Diagnose read latency

Run `node --env-file=.dev.vars scripts/solana/profile-gallery.mjs` for the same full inventory/decoding/quote path without signing or broadcasting. The local endpoint may differ from the hosted secret. Supply an authenticated Alchemy Devnet URL through private `SOLANA_RPC_URL` configuration to compare that provider; never print the complete URL or keys. The script logs only the provider hostname, RPC method/status/duration and aggregate result.

The data API logs sanitized per-method timings and total duration, and sends a `Server-Timing` header on success. Start with production Worker logs for timeouts. The browser budget is 40 seconds, above the server's 35-second total read budget; each read-only RPC attempt is bounded at 5 seconds, including response-body consumption, with one fresh retry. Request cancellation propagates to the RPC. Do not store live fetch promises in Worker globals: a canceled visitor must not leave later visitors waiting on its I/O. Only the footer's completed status values are cached. No D1, asset cache or periodic chain writes are introduced.

Regression checks: `node --test tests/read-budget.test.mjs tests/gallery-route.test.mjs tests/issuance.test.mjs tests/chase-receipts.test.mjs`, then TypeScript and the Sites build. The Worker route regression deliberately stalls one visitor and verifies a second receives its own checked result.

## Transient RPC recovery

Hosted logs on 2026-09-07 showed a 12-second `getAccountInfo` timeout among otherwise successful 227–307ms gallery reads. This is verified upstream intermittency; no inventory caching cause was established. Consumer read transports now use fresh 5-second attempt controllers and at most one retry after timeout/network errors or HTTP 408/429/500/502/503/504. The deadline covers body consumption. Retry-After is honored within the enclosing request budget. Caller cancellation stops both I/O and backoff. Validation/program errors and transaction sends are not retried by this mechanism. `tests/chain-read-retry.test.mjs` covers timeout, body stall, cancellation, HTTP503, bounded exhaustion and exclusion of semantic/write retries.

The browser retains only its last successful same-query view during refresh, with a visible last-verified time and failed-update label. This is component memory, not D1, browser persistence or a cross-visitor cache. Changed queries do not display old-query data; missing-state and nonpublic errors clear the view. The first transient failed page read retries after 1.5 seconds; later failures return to the 30-second cadence. Restart read cancels and supersedes the current request. Sequence/generation/slot guards reject obsolete completions; hidden tabs cancel work, and visibility or BFCache restoration restarts it.
