Join our community of builders on Discord!

Lightchain Studio Architecture

Lightchain Studio is a build of Code - OSS with Lightchain's own built-in extensions and a few patches to the Code - OSS source. All Lightchain features live in one extension, lightchain.lightchain-studio, written in TypeScript. This page describes Studio 0.1.0. Paths below are relative to the repository.

The pieces

PartPathWhat it is
The IDEsrc/, product.jsonCode - OSS, branded Lightchain Studio. The extension gallery is Open VSX; no update server is configured, so the app never updates itself.
Lightchain extensionextensions/lightchain-studio/Wallet, credits, Lightchain AI, Foundry, Deployed Programs, Interact, Start page, Lightchain Marketplace.
Solidityextensions/hardhat-solidity/Solidity language support, vendored from Nomic Foundation (MIT).
Themeextensions/theme-lightchain/The Lightchain Dark theme.
Defaultsextensions/lightchain-defaults/Default settings: tips and experiments off, walkthroughs don't open on install, no empty-editor hint, no chat in the command center.
Inside the Lightchain extension:
PathRole
src/networks.tsTestnet and mainnet: chain id, RPC, explorer, JobRegistry, Lightchain AI server and relay URLs.
src/lightchain/The Lightchain AI client: sign-in, sortition sessions, session keys, encrypted blobs, relay streaming, model list, stalled-job reclaim.
src/agent/The coding agent loop, its tools, the editor context, chats, inline edit, quick fixes, diffs.
src/aiView.tsThe Lightchain AI panel.
src/wallet.ts, src/keystore.tsLocal accounts, signing, the mainnet guard, Foundry keystore export.
src/browserBridge.ts, src/connectPage.tsThe browser wallet bridge and the page it serves.
src/credits.tsJobRegistry deposit, withdraw and delegate allowance.
src/foundry.ts, src/signingProxy.ts, src/deployWizard.tsForge tasks, the local signing proxy, Deploy Contract.
src/deployedPrograms.ts, src/interact.tsThe Deployed Programs view and the Interact tab.
The extension activates on startup. Its compiled bundle (dist/extension.js) is committed, so the IDE runs right after npm run compile.
CodeHTML

Lightchain AI client

src/lightchain/inference.ts is a Node port of the protocol flow the Lightchain AI chat runs in the browser. One instance is one chat session with one worker. It talks to the Lightchain AI server (https://chat-api.testnet.lightchain.ai or https://chat-api.mainnet.lightchain.ai) and its relay (wss://relay.testnet.lightchain.ai/ws or wss://relay.mainnet.lightchain.ai/ws). Unlike the Developer API, the session key is made and kept in the IDE: the server only ever handles ciphertext of your prompts and answers.

1. Models

GET /api/models lists the models the network serves (name and bytes32 id), and GET /api/workers/availability?models=<ids> gives each one's eligible workers, free slots and status. Studio offers only the models in its coding allow-list (src/lightchain/models.ts).

2. Sign-in

Studio signs in with Sign-In with Ethereum and gets a JWT:
AccountMessage
Browser walletGET /api/auth/nonce, then Studio builds the SIWE message itself: domain localhost:3000, URI http://localhost:3000, the network's chain id, the statement "Sign in to Lightchain AI from Lightchain Studio. This does not send a transaction or cost gas.", and a 5-minute expiry.
Local accountGET /api/auth/challenge?address=<address> returns the message to sign.
The account signs it with personal_sign (EIP-191), and POST /api/auth/verify with { message, signature } returns { token, expiresAt }. The token goes to the IDE's secret storage under the key <chainId>:<address> and is reused by every chat until less than a minute of it is left. A 401 from the server drops it, and the next call signs in again. The server accepts SIWE messages for the domain localhost:3000 only. That is why the wallet bridge serves its page from http://localhost:3000 when that port is free: the wallet then sees a request from the same domain the message names, and shows no domain warning.

3. Session

CodeHTML
The first call asks the network to select a worker for the model by sortition; Studio waits up to 150 seconds for it. Studio then generates a random 32-byte session key and seals it to the worker's P-256 public key, and to the disputer's key when there is one:
  • an ephemeral P-256 key pair; ECDH with the recipient's key; the raw 32-byte shared secret (no KDF) is the AES-256-GCM key;
  • sealed key = ephemeralPublicKey (65 bytes) ‖ nonce (12) ‖ ciphertext ‖ tag (16).
The crypto module (src/lightchain/crypto.ts) is vendored unchanged from the Lightchain AI chat and is wire-compatible with the protocol's Go implementation.
Status on requestMeaning in Studio
408, 503, 504No worker is available for the model right now.
403The wallet has not authorized the server's delegate in JobRegistry; the body names the delegate. The panel offers Allow.
402Not enough credits.

4. A prompt

CodeHTML
Each prompt is encrypted with the session key (nonce (12) ‖ ciphertext ‖ tag (16)), uploaded as a blob, and submitted as one job on chain whose fee is debited from your prepaid balance by the server's delegate. A 5xx on submit means the server's submit transaction failed, so nothing was charged; Studio retries once after 2 seconds, and if that fails too, tells you no credits were charged. Studio sends only the newest message in each job; the worker rebuilds the conversation from the session's earlier jobs. The first prompt of a session also carries the agent's instructions.

5. Streaming

The relay sends JSON frames: chunk, complete, metadata, error, reassignment_required, reassigned, closed. Studio keeps only frames for its job id, decrypts each chunk payload with the session key and streams it into the panel. complete carries the full answer, and ends the job. A chunk with seq 1 after text has arrived means the worker restarted the job, and the stream starts over.
ConditionResult
No frame within 135 s of submittingThe worker is treated as failed.
No frame for 120 s mid-answerThe worker is treated as failed.
error frameThe worker is treated as failed (a rate-limit frame for a dropped chunk is ignored).
reassignment_required or closedThe session has ended; start a new chat.
When a worker fails, the panel retries once on another model with free workers, or on a new session for the same model.

Stalled jobs

A job whose worker accepted it and never answered keeps its fee escrowed in JobRegistry. Studio remembers the job ids (src/lightchain/stuckJobs.ts), reads each with getJob(jobId), and simulates settleStuckJob(jobId) to find out whether the contract will settle it yet (it can once the job's deadline plus the resolution timeout, 48 hours today, has passed). Settling is a no-fault exit: the worker is not slashed. Those that would succeed are offered for reclaim; each is one settleStuckJob transaction that returns the fee to your prepaid balance. Jobs that settle in another way are forgotten.

The coding agent

Lightchain models have no native function calling, so the agent (src/agent/agent.ts) uses a text protocol. The system prompt lists the tools, and the model calls one with a fenced block:
CodeHTML
The agent runs each call, appends the results to the conversation as the next user message, and asks again, until the model answers without a tool call.
RuleValue
Steps per request12
Stop after consecutive all-failed steps3
Automatic build-and-fix rounds2 (after Solidity files were written)
read_file limit60,000 characters
Tool output limit12,000 characters
forge_build and run_command timeout180 s
There is no partial-edit tool: models often get "replace this exact text" wrong, so a change is always read_file, then write_file with the complete new file, which you approve as a diff. Two habits of small models are handled: a reply that contains a code block and a file name instead of a tool call is turned into a write_file proposal (still approved by you), and a reply that claims a file was created without calling write_file gets one reminder to actually write it. File tools resolve every path inside the project root and refuse anything that resolves outside it, symbolic links included. run_command runs, after your approval, through your login shell ($SHELL -lc) on macOS and Linux, or cmd.exe /d /s /c on Windows, in the project root.

Wallet

src/wallet.ts holds two kinds of accounts:
  • Local: the private key in the IDE's secret storage (Electron safeStorage, backed by the OS key store), under lightchain.wallet.key.<id>. Signing happens in the extension process with viem. A recovery phrase is used only to derive the key, which is then stored; the phrase is not.
  • Browser: only the address and wallet name are stored. Every signature and transaction goes through the browser wallet bridge.
Every transaction goes through confirmMainnet first. On mainnet it estimates the fee and shows a modal confirmation; declining throws before anything is signed. Foundry keystore export (src/keystore.ts) writes Web3 Secret Storage v3: scrypt (N = 8192, r = 8, p = 1), AES-128-CTR, MAC = keccak256(derived key bytes 16–32 ‖ ciphertext), the parameters Foundry itself writes. The file is created with mode 0600 in ~/.foundry/keystores (mode 0700).

Browser wallet bridge

src/browserBridge.ts connects any browser wallet with no WalletConnect project ID, the same approach as Foundry's --browser mode. Studio runs a small HTTP server on the loopback interface, and the page it serves talks to the wallet extension:
RouteUse
GET /connect/<token>The connect page.
GET /connect/<token>/eventsServer-sent events: requests from the IDE to the page (heartbeat every 15 s).
POST /connect/<token>/messageResults from the page to the IDE (JSON, at most 256 KiB).
GET /assets/<name>A fixed list of fonts, the logo and the stylesheet.
  • Origin. http://localhost:3000 when nothing answers on port 3000, else http://127.0.0.1:<port> with a port chosen once and saved, so wallets keep their site permission across restarts. The server binds to 127.0.0.1 either way.
  • Token. 256 random bits, created once per install and kept in secret storage; compared in constant time. A wrong token gets 404.
  • Host and Origin. The Host header must be exactly the bridge's origin (421 otherwise), which blocks DNS rebinding. An Origin header, when present, must be the same origin (403 otherwise), which blocks other sites.
  • The page is sent with a strict Content-Security-Policy (default-src 'none', a per-response script nonce, connect-src 'self', frame-ancestors 'none'), no-store and no-referrer. It finds wallets through EIP-6963, falling back to window.ethereum. Before eth_sendTransaction or eth_signTypedData_v4 it calls wallet_switchEthereumChain, and wallet_addEthereumChain if the wallet doesn't know the network. It refuses a request when the wallet's accounts don't include the address Studio expects.
  • Timeouts. A request waits up to 5 minutes for the wallet. If no page is open, Studio opens one and waits up to 90 seconds for it to connect.

The signing proxy

Forge has to send transactions, and Studio must not hand it a key. src/signingProxy.ts starts one JSON-RPC server per deploy and passes it to forge:
CodeBASH
MethodHandling
eth_accounts, eth_requestAccountsThe active account.
eth_sendTransactionSigned and sent by the active account exactly as forge built it (nonce, gas, fees), through the mainnet guard. A different from is refused.
eth_sign, personal_sign, eth_signTypedData_v4Refused.
Anything elseForwarded to the network's RPC.
The proxy binds to 127.0.0.1 on a random port, has a 192-bit random token in its path, answers only POST, and rejects any request that carries an Origin header, so a web page can't reach it. It closes when the forge task ends. Deploy & Verify adds --verify --verifier blockscout --verifier-url <explorer>/api/.

Deployed Programs and Interact

Deployed Programs (src/deployedPrograms.ts) merges, per project and per network:
  • Foundry broadcasts: **/broadcast/**/<chainId>/run-latest.json, every CREATE and CREATE2 transaction and its additionalContracts, watched for changes;
  • contracts created by the deployer address, from Lightscan's Etherscan-compatible API: GET <explorer>/api?module=account&action=txlist&address=<deployer>&sort=desc&page=1&offset=1000, keeping transactions with no to that did not fail; the last result is cached so the list survives being offline;
  • addresses you added.
To mark which contracts the project uses, it scans up to 5,000 files of at most 1 MB each (binary files and lightchain.programs.scanExclude skipped) for their addresses. Interact (src/interact.ts) takes the ABI from, in order: the Foundry artifact named like the contract; any Foundry artifact whose runtime bytecode equals the code on chain once the Solidity metadata is stripped; Lightscan's verified source (GET <explorer>/api/v2/smart-contracts/<address>); a JSON file you choose. Reads are eth_call. Writes are first run as eth_call from your account, so a revert shows before you sign.

What Studio stores

DataWhere
Local private keysSecret storage
Lightchain AI sign-in tokens (one hour)Secret storage, per chain id and address
Wallet bridge tokenSecret storage
Account names and addresses, the active account, the browser wallet's address, the bridge port, the chosen model, stalled job idsStudio's global state
The project's network, Deployed Programs lists and deployer, chatsThe project's workspace state
The Lightchain extension itself connects only to the Lightchain AI server and relay, the Lightchain RPC, Lightscan's API, the faucet (https://lightfaucet.ai) when you claim from the panel, and the marketplace catalog URL if you set one.

Security model

  • Prompts and answers are encrypted end to end. The session key is generated in the IDE and sealed to the selected worker (and the disputer). The server and relay carry only ciphertext.
  • Keys stay in secret storage. Local keys are used in the extension process and never reach a web view, the terminal or forge. Exporting a key or sending a mainnet transaction always asks first.
  • Forge never holds a key. Deploys sign through the loopback proxy, which only signs eth_sendTransaction for the active account and refuses requests from browsers.
  • The wallet bridge is loopback only, with a per-install 256-bit token and exact Host and Origin checks.
  • The agent asks before it changes anything. It reads and searches the project and runs forge build on its own; every file write and every command needs your approval, and it does not run in Restricted Mode.
  • Credits are capped. The server can spend from your prepaid balance only up to the allowance you set for its delegate, and you can withdraw the balance at any time from your own wallet.

Build from source

You need Node.js 24, Python 3, the platform's build tools for native modules, and Foundry to use the Foundry features.
CodeBASH
After changing the Lightchain extension's sources:
CodeBASH
Then run Developer: Reload Window. The repository's README lists the platform build tools and the packaging commands; pushing a v* tag runs the release workflow, which builds every platform and publishes a GitHub Release.