Project Template
interfold init makes a project from the default template (templates/default in the Interfold
repository). This page is for developers who change that project. It describes what interfold init
does, the folders and scripts of the project, its configuration, and the file to edit for each part.
To run the template first, see the Quick Start.
What interfold init does
interfold init [PATH] [--template <URL>] [--skip-install] [--skip-cleanup]| Argument or option | Effect |
|---|---|
PATH | The project folder. The folder must be empty or must not exist. Without PATH, the command uses the current folder. |
--template <URL> | The template source, in the form git+https://github.com/theinterfold/interfold.git#main:templates/default. The part after # is the branch or tag and the path. |
--skip-install | Do not run pnpm install. Use this option for an offline setup or for release candidates that are not published. |
--skip-cleanup | Keep the project folder if the command fails. This option is mainly for tests of the installer. |
Without --template, the command uses tag v<CLI version> of
https://github.com/theinterfold/interfold.git. For CLI 0.18.0, the tag is v0.18.0. The command
does these operations in sequence:
- It makes a shallow clone of the repository in
/tmp/__interfold-tmp-folder.1. - It copies the template to the project folder. In each
package.json, it replaces the workspace versions of@interfold/contracts,@interfold/react, and@interfold/sdkwith the versions of the cloned release. In eachCargo.toml, including the guest's, it replaces every path dependency on an Interfold crate (e3-program-runner,e3-bfv-client,e3-fhe-params,e3-compute-provider,e3-openvm-types, ande3-safe) with a Git dependency at the cloned commit. - It replaces
.gitignorewith.gitignore.bak, renamespnpm-workspace.yaml.baktopnpm-workspace.yaml, and deleteslib/. - It runs
git init. - It runs
pnpm install, unless you give--skip-install. - It commits all files with the message
Initial Commit.
interfold init does not run when the CLI finds an interfold.config.yaml in the current folder,
in a parent folder, or in the default configuration folder. In that case, it stops with
Cannot run `interfold init` when a configuration exists.
Layout
The template copier uses e3-fs, a small async filesystem adapter. Production copying uses the
physical filesystem; tests can use the same operations over an in-memory root. That test seam does
not make copying or replacement an atomic filesystem transaction.
- Cargo.toml
- deployed_contracts.json
- flake.nix
- hardhat.config.ts
- interfold.config.yaml
- package.json
- pnpm-workspace.yaml
- remappings.txt
- rust-toolchain.toml
| Path | Contents |
|---|---|
program/ | The Rust crate e3-user-program. src/lib.rs exports fhe_processor (the FHE computation) and policy (the input policy). |
guest/ | The OpenVM guest that proves program/. A Cargo workspace of its own; openvm.toml lists the VM extensions. |
contracts/ | MyProgram.sol, the E3 program contract, and Mocks/MockOpenVmReceiptVerifier.sol, a verifier that accepts every receipt, for the local chain only. |
deploy/default.ts | deployTemplate, which deploys the receipt verifier and MyProgram, registers MyProgram on the Interfold contract, and writes the addresses to the config. |
scripts/ | The shell scripts behind the dev:* scripts, the local deploy entry point deploy-local.ts, and anvil-automine.mjs. |
server/ | The coordination server (index.ts) and the client for the program server (runner.ts). |
client/ | The React and Vite web client. It is the only package in pnpm-workspace.yaml. |
tests/ | The end-to-end test integration.spec.ts and its Anvil helpers. |
runner/ | The crate e3-user-program-runner. Its program-runner binary serves program/ as the development runner or the OpenVM proving service. |
Cargo.toml | The Cargo workspace. Its members are program and runner. |
deployed_contracts.json | The deployment records that the deploy scripts read and write. |
flake.nix | A Nix development shell with Rust 1.91.1, Node.js, and pnpm. .envrc loads it with direnv. |
A project that has an .interfold/support folder cannot run interfold program. To migrate it,
delete .interfold/support and its two workspace members from Cargo.toml. Then copy runner/
from the template and add runner to the workspace members. In [workspace.dependencies],
replace e3-program-server with e3-program-runner ={' '} {((git = 'https://github.com/theinterfold/interfold'), (rev = '<rev>'))}. Use the same rev as
the other e3-* dependencies of the project.
The scripts make more folders when they run:
.interfold/config/and.interfold/data/: the ciphernode keys and databases..interfold/noir/:bband the circuits thatinterfold noir setupinstalls..interfold/data-availability/: the output ciphertexts that the coordination server serves..interfold/caches/openvm/: the guest build, its proving keys, the receipt identity, and the worker configuration thatinterfold program compilewrites.types/,artifacts/, andcache/: the Hardhat and TypeChain output.target/: the Cargo output.
The .gitignore of the project ignores .interfold/, artifacts/, cache/, target/,
node_modules, and .env files.
Scripts
package.json in the project root defines these scripts. Run a script with pnpm run <name>, for
example pnpm run dev:all. The other pages use the short form, for example pnpm dev:all.
| Script | Command | What it does |
|---|---|---|
dev:all | ./scripts/dev_all.sh | Starts the full local stack. See What runs. With --tmux, it runs scripts/dev_all_tmux.sh instead. |
predev:all | ./scripts/compile_program.sh | Runs interfold program compile with the local settings, before dev:all. |
dev:setup | bash ./scripts/setup.sh | Runs pnpm install --frozen-lockfile, cargo build, interfold program compile, pnpm compile, and interfold noir setup. It stops if no interfold CLI is installed. Inside an Interfold repository checkout, it also builds the circuits and installs the CLI from source. |
dev:evm | hardhat node | Starts a Hardhat node on port 8545. dev:all uses Anvil instead. Only the tmux mode uses this script. |
dev:ciphernodes | ./scripts/dev_ciphernodes.sh | Waits for the chain. Sets the ciphernode wallets, runs interfold noir setup, deploys the contracts, starts the five nodes, and registers them. |
dev:server | ./scripts/dev_server.sh | Waits for the ciphernodes, sets the server environment, and starts server/index.ts with tsx. |
dev:program | ./scripts/dev_program.sh | Waits for the ciphernodes and runs interfold program start. |
dev:frontend | ./scripts/dev_frontend.sh | Waits for the chain and the program server. Exports the VITE_ variables and starts the client on port 3000. |
compile | hardhat compile | Compiles the Solidity contracts and generates the TypeChain types in types/. |
deploy | pnpm clean:deployments && hardhat run scripts/deploy-local.ts --network localhost | Deletes the local deployment records, then deploys the protocol and MyProgram to localhost. Run it with pnpm run deploy, because pnpm deploy is a built-in pnpm command. |
deploy:dev | hardhat run scripts/deploy-local.ts --network localhost | Deploys the protocol and MyProgram to localhost. It does not run clean:deployments first. |
clean:deployments | hardhat utils:clean-deployments | Deletes the deployment records for a network. The default network is localhost. |
lint | eslint . | Runs ESLint. |
test | hardhat test | Runs the Hardhat test runner. |
test:server | vitest run ./server/runner.test.ts | Runs the unit tests of the program server client. |
test:integration | pnpm test:server && ./scripts/test_integration.sh | Runs the server tests, then starts the stack and runs tests/integration.spec.ts. It needs a CLI built with the test-only-skip-proof-aggregation feature. |
The client package (client/package.json) has its own scripts: dev (vite --no-open --host),
build, preview, format, and format:check.
interfold.config.yaml
interfold.config.yaml configures the local chain, the program mode, and the local ciphernodes. The
CLI reads it from the current folder or from a parent folder. The template file has this structure.
The contract addresses, the deploy blocks, and the node addresses are placeholders here. The deploy
script writes the contract values.
chains:
- name: 'localhost'
rpc_url: 'ws://localhost:8545'
ingestion_confirmations: 0
rpc_poll_interval_ms: 250
data_availability:
mode: mock_http
rpc_url: http://127.0.0.1:8080/availability
contracts:
e3_program:
address: '<address>'
deploy_block: <block>
interfold:
address: '<address>'
deploy_block: <block>
# ciphernode_registry, bonding_registry, slashing_manager, and fee_token
# have the same two keys.
program:
dev: false
nodes:
cn1:
address: '<Anvil account 1>'
quic_port: 9201
ctrl_port: 50501
autonetkey: true
autopassword: true
dashboard_port: 8004
cn2:
address: '<Anvil account 2>'
quic_port: 9202
ctrl_port: 50502
autonetkey: true
autopassword: true
# cn3, cn4, and cn5 follow the same pattern (ports 9203 to 9205 and 50503 to 50505).| Key | Template value | Meaning |
|---|---|---|
chains[].name | localhost | The chain name. interfold print-env --chain localhost and the deploy scripts use this name. |
chains[].rpc_url | ws://localhost:8545 | The RPC endpoint of the chain. |
chains[].ingestion_confirmations | 0 | Confirmations before a node applies EVM logs. The default is 3. Use 0 only for a single-process development chain such as Anvil. |
chains[].rpc_poll_interval_ms | 250 | Milliseconds between the polls of an HTTP provider for a receipt or a new block. The default is 7000. Use 250 only for a single-process development chain such as Anvil. |
chains[].data_availability.mode | mock_http | Where nodes fetch the output ciphertext. mock_http reads it from a local HTTP service. The other mode is avail. |
chains[].data_availability.rpc_url | http://127.0.0.1:8080/availability | The coordination server. It serves each object at /availability/objects/<contentHash>. |
chains[].contracts.<name> | address, deploy_block | The contract address and the block to read events from. deploy/default.ts writes these values for the contracts that it deploys. |
program.dev | false | false proves with OpenVM. true runs the development runner natively, without a proof. The local scripts set E3_PROGRAM__DEV=true unless TEMPLATE_REAL_PROOFS=1. |
program.openvm | Commented out | The OpenVM workers and backend for dev: false. See Compute Provider. |
nodes.<name>.address | Anvil accounts 1 to 5 | The Ethereum address of the ciphernode. |
nodes.<name>.quic_port | 9201 to 9205 | The QUIC port for peer-to-peer traffic. |
nodes.<name>.ctrl_port | 50501 to 50505 | The port of the control socket that the CLI uses to talk to a running node. |
nodes.<name>.autonetkey | true | Generate a network key on start if none is set. |
nodes.<name>.autopassword | true | Generate a password on start if none is set. |
nodes.<name>.dashboard_port | 8004 (cn1 only) | Serves the node dashboard at http://127.0.0.1:8004. |
interfold program compile and interfold program start also accept --dev true or --dev false.
The flag overrides program.dev for that one command. The local scripts load
scripts/lib/dev_config.sh, which sets E3_PROGRAM__DEV=true and TEMPLATE_UNPROVED_TEST=1 unless
TEMPLATE_REAL_PROOFS=1. TEMPLATE_UNPROVED_TEST=1 makes deployTemplate deploy
MockOpenVmReceiptVerifier, and it works only on chain ID 31337.
The CLI keeps the node data next to this file: keys in .interfold/config/<node>, databases in
.interfold/data/<node>, and the prover files in .interfold/noir/. An environment variable with
the E3_ prefix overrides a key. Two underscores separate the levels, for example
E3_NODES__CN1__SKIP_PROOF_AGGREGATION=true. For every key, see the
configuration reference.
Environment variables
The template ships no .env files. The scripts set the environment when they start each process.
Client. scripts/dev_frontend.sh exports the output of
interfold print-env --vite --chain localhost. If a required variable is missing, the client shows
Environment Configuration Required. You can also set these variables in client/.env.
| Variable | Required | Source or default |
|---|---|---|
VITE_INTERFOLD_ADDRESS | Yes | chains[].contracts.interfold |
VITE_E3_PROGRAM_ADDRESS | Yes | chains[].contracts.e3_program |
VITE_REGISTRY_ADDRESS | Yes | chains[].contracts.ciphernode_registry |
VITE_BONDING_REGISTRY_ADDRESS | Yes | chains[].contracts.bonding_registry |
VITE_FEE_TOKEN_ADDRESS | Yes | chains[].contracts.fee_token |
VITE_RPC_URL | No | chains[].rpc_url. Default: http://localhost:8545. |
VITE_THRESHOLD_BFV_PARAMS_PRESET_NAME | No | Default: INSECURE_THRESHOLD_64. |
VITE_WALLETCONNECT_PROJECT_ID | No | A WalletConnect project ID for ConnectKit. |
Coordination server. scripts/dev_server.sh sets PRIVATE_KEY (Anvil account 0),
CHAIN_ID=31337, and the output of interfold print-env --chain localhost: INTERFOLD_ADDRESS,
RPC_URL, REGISTRY_ADDRESS, BONDING_REGISTRY_ADDRESS, E3_PROGRAM_ADDRESS, and
FEE_TOKEN_ADDRESS. Then it sets RPC_URL=http://localhost:8545. These optional variables have
defaults:
| Variable | Default | Use |
|---|---|---|
PORT | 8080 | The port of the coordination server. |
PROGRAM_RUNNER_URL | http://127.0.0.1:13151 | The program server. The server posts each job to /run_compute. |
CALLBACK_URL | http://127.0.0.1:8080 | The URL that the program server calls with the result. |
DATA_AVAILABILITY_DIRECTORY | .interfold/data-availability | The folder for the output ciphertexts that the server serves. |
TEST_MODE | Not set | When set, the server adds a GET /test route. dev:all sets it. |
Hardhat. hardhat.config.ts loads a .env file from the project root. It reads MNEMONIC
(default: the Anvil test mnemonic), PRIVATE_KEY, RPC_URL, and block explorer API keys, for
example ETHERSCAN_API_KEY.
Dev scripts. scripts/lib/dev_config.sh reads BFV_PRESET (insecure-64 or secure-8192,
default insecure-64) and COMMITTEE (default minimum). These variables have an effect only when
the template is inside an Interfold repository checkout, where the scripts build the circuits.
scripts/anvil-automine.mjs reads ANVIL_RPC_URL (default http://127.0.0.1:8545).
Where to change things
| To change | Edit |
|---|---|
| The FHE computation | fhe_processor in program/src/lib.rs. See Hello World. |
| Which inputs the computation uses | policy in program/src/lib.rs. The default policy uses every input. |
| Input checks and proof verification | publishInput, validate, verify, and verifyDataAvailability in contracts/MyProgram.sol. |
| How the contracts are deployed | deploy/default.ts. scripts/deploy-local.ts calls it after it deploys the protocol with mocks. |
| The committee size, input window, and parameter set | handleRequestComputation in client/src/pages/steps/RequestComputation.tsx. It uses DEFAULT_E3_CONFIG.committeeSize, a 100-second window, and paramSet: 0. |
| How the client encrypts and publishes inputs | client/src/pages/steps/EnterInputs.tsx and client/src/utils/input.ts. |
| How the client shows the result | client/src/pages/steps/EncryptSubmit.tsx and client/src/pages/steps/Results.tsx. |
| The SDK setup | client/src/utils/sdk-config.ts and client/src/context/WizardContext.tsx (client), createPrivateSDK in server/index.ts (server). |
| When the computation starts and how the output is published | server/index.ts. The server schedules the job when the input window closes. It skips an E3 with fewer than two inputs. |
| The program server and its proof | runner/src/main.rs calls e3_program_runner::run. program-runner dev is unproved. program-runner openvm is the OpenVM service. |
| What the guest proves with | guest/openvm.toml (the VM extensions) and the openvm-hashes feature of program/Cargo.toml. A change gives a new receipt identity. |
| The local ciphernodes | The nodes section of interfold.config.yaml and scripts/dev_ciphernodes.sh. Both files must list the same nodes. |
| The local chain | The anvil command in scripts/dev_all_concurrently.sh and scripts/anvil-automine.mjs. |
Change the ciphernode set
scripts/dev_ciphernodes.sh has one private key per node (PRIVATE_KEY_CN1 to PRIVATE_KEY_CN5).
It reads each node address from interfold.config.yaml, expects five nodes to start
(EXPECTED_NODES=5), and registers each address with ciphernode:admin-add. To add or remove a
node, change all of these items together with the nodes section of interfold.config.yaml.
The template is for local development. MyProgram.publishInput does not prove that a ciphertext
matches its commitment, and verifyDataAvailability is a local mock. A production E3 program must
do both checks. The CRISP program (opens in a new tab) shows
one approach. The program server does not authenticate callers, so it is not safe on an untrusted
network.