FINCHLaunch App

Quickstart

Finch is a TypeScript-first monorepo: a Next.js app (this site), four packages — @finch/sdk, @finch/providers, @finch/flightpath, @finch/db — and Foundry contracts.

setup
# in the monorepo root
npm install

# server-side environment (never client-side)
# GROQ_API_KEY=…             model compute (free tier); OPENROUTER_API_KEY=… also free
# HYPERBOLIC_API_KEY=…       paid alternative, used only when no free provider is set
# MONGODB_URI=…              optional: registry + memory + ledgers

npm run dev        # web app on http://localhost:3000
npm run typecheck  # all workspaces

Then hatch your first finch — observer mode, read-only, safe by default:

first-flight.ts
import { createFinch, hyperbolic } from "@finch/sdk";

const nest = await createFinch("first-flight")
  .describe("Reads balances and reports. Nothing more.")
  .model(hyperbolic("meta-llama/Llama-3.3-70B-Instruct"))
  .memory({ kind: "ephemeral" })
  .tools("balance_native", "token_data")
  .wallet({ mode: "observer" }) // read-only — the safe default
  .hatch();

const result = await nest.run(
  "What is the native balance of 0x000000000000000000000000000000000000dEaD?",
);
console.log(result.output);
console.log(result.steps); // every model + tool step, logged

Finch SDK

A finch is a portable manifest — finch.json (finch.manifest/0.1): identity, model, memory, tools, permissions, wallet, triggers, budget, deployment, publisher, endpoints, IO schemas. The fluent builder and the visual Finch Builder emit the same document, and hatch() resolves it against live infrastructure. Manifests cannot widen their own permissions: write tools are stripped unless the wallet grants operator mode, RWA interactions are always registry-limited, and simulation is not optional.

hatch a builder-made manifest
// Hatch a manifest built in the visual Nest Builder (/app/build):
import manifest from "./market-watcher.manifest.json";
import { hatchFromManifest, hyperbolic } from "@finch/sdk";

const nest = await hatchFromManifest(manifest, {
  provider: hyperbolic(manifest.model.model),
});

Runs return a full trace: output, steps, executions (Flightpath records), usage, and a haltedBy reason — including the kill switch.

Flightpath — Robinhood Chain execution

Flightpath is the EVM execution layer: balances, transfers, ERC20s, contract reads/writes, swaps, Pons fee accounting, token and portfolio data, and approved RWA interactions. Every write follows one path — policy → simulate → (approve) → submit → confirm → log — and produces an idempotent ExecutionRecord.

operator mode with allowances
// Operator mode: bounded writes. The key comes from the RUNTIME env —
// it is never part of a manifest and never the treasury key.
import { createFlightpath } from "@finch/flightpath";
import { createFinch, hyperbolic } from "@finch/sdk";

const flightpath = createFlightpath({
  operatorKey: process.env.FLIGHTPATH_OPERATOR_KEY as `0x${string}`,
});

const nest = await createFinch("payments-runner")
  .model(hyperbolic("Qwen/Qwen3-235B-A22B"))
  .tools("balance_native", "transfer_native")
  .wallet({
    mode: "operator",
    allowances: [{ asset: "native", perDay: "0.1", perTx: "0.02" }],
    allowedRecipients: ["0xRecipientYouTrust00000000000000000000000"],
    approvalThreshold: 0.5,
  })
  .hatch({ flightpath });

// every write: policy → simulate → (approval) → submit → confirm → log

Flightpath targets Robinhood Chain mainnet by default: chain 4663, an Arbitrum Nitro L2 with ETH as its native currency, reached over rpc.mainnet.chain.robinhood.com with a Blockscout explorer — all baked into @finch/flightpath. The NEXT_PUBLIC_ROBINHOOD_* and ROBINHOOD_RPC_URLS variables are overrides for dedicated providers; FLIGHTPATH_FORCE_DEV=1 switches to a labelled dev chain for fork testing. Pons V2's contracts are verified on the explorer and $FINCH is live — the $FINCH section lists what Flightpath reads from them.

Model providers

The model layer is a provider abstraction. Hyperbolic serves compute first (hyperbolic(model)), and openAICompatible({…}) binds any standard endpoint — Finch is never permanently coupled to one vendor. Providers are server-side only; keys never reach a browser or an agent's own context.

Model compute — free by default

A finch names a model; the environment decides who serves it. Every provider below speaks the same OpenAI-compatible shape, so switching is configuration rather than an integration — which is the point of keeping the model layer abstract.

providercostenvnotes
Groqfree-tierGROQ_API_KEYFree tier with rate limits; very fast inference. Good default for public preview traffic.
Cerebrasfree-tierCEREBRAS_API_KEYFree tier with daily limits; fastest token throughput of the hosted options.
OpenRouterfree-tierOPENROUTER_API_KEYRoutes to many models; :free variants cost nothing but are rate limited and can be busy.
Google AI Studiofree-tierGEMINI_API_KEYFree tier via the OpenAI-compatible endpoint; generous limits for read-only workloads.
Ollama (local)localENABLE_OLLAMAFully free and fully private — runs on your own machine. No key, no quota, no per-request cost.
Together AIpaidTOGETHER_API_KEYPaid per token, with occasional free models.
HyperbolicpaidHYPERBOLIC_API_KEYPaid per token; open-model serverless inference.

Selection prefers free tiers first, then local, then paid — so an operator who sets only GROQ_API_KEY gets working previews at no per-visitor cost. FINCH_PROVIDER overrides the order. The app header shows which provider is actually serving, and with none configured previews refuse rather than fabricate a response.

Model ids are env-overridable (GROQ_MODEL, OLLAMA_MODEL, …) because provider catalogs change faster than this page does — treat the defaults as a starting point, not a guarantee.

Data layer — MongoDB

@finch/db owns operational data: finches, nests, Aviary listings, execution records, vector memory, Pons fee events, the public treasury ledger, double-entry compute credits, service-call metering and hashed API keys. Unique indexes double as idempotency guarantees. Without MONGODB_URI, the site serves labeled seed data read-only; scripts: npm run seed -w @finch/db, npm run indexes -w @finch/db.

Permission model

Every write an agent attempts is evaluated against these rules, in this order, before anything is simulated or signed. The table is generated from POLICY_RULES in @finch/flightpath, and a test asserts that every rule the engine can actually emit appears here — so this cannot drift away from the code.

ruleverdicttriggers whenwhy
wallet.modedenyThe wallet is not in operator mode.Observer and none-mode finches have no write authority at all. This is the default, so a finch is read-only until you deliberately grant otherwise.
recipients.allowlistdenyA counterparty is not on allowedRecipients (when that list is set).Counterparty means whoever ends up able to move value: a transfer's recipient, an approval's spender, an RWA action's other side — not just transfer destinations.
contracts.allowlistdenyA contract call, swap, approval or ERC20 transfer targets a contract outside allowedContracts.Anything that hands calldata to a contract must name that contract up front, approvals included.
rwa.approveddenyAn RWA interaction targets an asset outside the approved registry.Permissioned real-world assets are gated to an explicit registry, and a manifest cannot waive the gate.
rwa.contractsdenyRWA registry gating was opted out of and the target is not an allowlisted contract.Even with the registry gate off, the target must still be named.
allowance.missingdenyThe intent spends an asset with no configured allowance.Spending authority is opt-in per asset. No allowance means no spend.
allowance.perTxdenyA single spend exceeds the per-transaction cap.Caps the blast radius of any one mistake, independently of the daily budget.
allowance.dailydenyThe rolling 24h spend would exceed perDay.Spend is debited at submission, so a transaction that broadcasts always counts even if its receipt is lost.
allowance.approvalThresholdneeds_approvalA spend exceeds approvalThreshold as a fraction of the daily allowance.Parks the intent at awaiting_approval. Only a recorded human approval releases it — replaying the execution id will not.
human.approvalallowA human approved a parked intent.Recorded on the execution as who approved and when, and covered by the Proof of Flight hash.
defaultallowEvery check above passed.The intent proceeds to mandatory simulation before anything is signed.

Two of these cannot be configured away. RWA interactions are always gated to the approved registry, and simulation always runs before signing — a manifest has no field that disables either.

Running a finch yourself

A hatched manifest does not run on Finch — it runs wherever you point a runtime at it. That is the whole portability claim, so here is the entrypoint, in full.

run-finch.ts — the entire host
// node --experimental-strip-types run-finch.ts
import { readFileSync } from "node:fs";
import { hatchFromManifest, hyperbolic } from "@finch/sdk";
import { createFlightpath } from "@finch/flightpath";

const manifest = JSON.parse(readFileSync("./market-scout.finch.json", "utf8"));

// Observer Flightpath: real Robinhood Chain reads, no signer, writes denied.
// Pass operatorKey here — and only here — to grant bounded write authority.
const flightpath = createFlightpath({ agentId: manifest.identity.handle });

const finch = await hatchFromManifest(manifest, {
  provider: hyperbolic(manifest.model.model),   // HYPERBOLIC_API_KEY from env
  flightpath,
});

const result = await finch.run("What is the head block on Robinhood Chain?");
console.log(result.output);
console.log(result.steps);            // every model + tool step
console.log(result.usage);            // tokens in / out
console.log(finch.unresolvedServices); // services the manifest declared but nothing resolved

Today the packages are consumed from the repo rather than from npm: clone it, or vendor packages/sdk, packages/providers and packages/flightpath into your project. They contain no TypeScript that needs compiling away — parameter properties and decorators are deliberately avoided — so node --experimental-strip-types runs them straight from source with no build step. Published npm packages are the next step, not a claim we are making now.

Triggers. A manifest can declare cron and webhook triggers, and they travel with it — but Finch does not schedule or receive them. Your host does: read manifest.triggers and wire them to your own scheduler or route handler. Manual is the only trigger this product acts on.

acting on a declared trigger
// Finch records triggers; your host acts on them.
for (const trigger of manifest.triggers) {
  if (trigger.kind === "cron") {
    schedule(trigger.schedule, () => finch.run("scheduled tick"));
  }
  if (trigger.kind === "webhook") {
    app.post(`/hooks/${trigger.slug}`, async (req, res) => {
      res.json(await finch.run(JSON.stringify(req.body)));
    });
  }
}

Proof of Flight

An operator can claim anything about what their agent did. A Proof of Flight is the smallest set of facts that lets someone else check the claim: which finch acted, under which policy, in which transaction, in which block — plus a SHA-256 over exactly those facts, so the receipt cannot be quietly edited afterwards.

proof-of-flight/0.1
{
  "version":     "proof-of-flight/0.1",
  "finchId":     "execution-finch",       // which agent acted
  "nestId":      "pons-intelligence",     // set when it came from a nest task
  "taskId":      "t4",
  "action":      "transfer.native",
  "summary":     "transfer 0.01 ETH → 0x…",
  "chainId":     4663,
  "txHash":      "0x…",                   // where it happened
  "blockNumber": "53000000",
  "gasUsed":     "21000",
  "policy":      { "verdict": "allow", "rule": "default" },
  "approval":    { "approvedBy": "operator@finch", "at": "…" },   // if a human released it
  "simulation":  { "ok": true, "gasEstimate": "21000" },
  "confirmedAt": "2026-09-03T00:00:03.000Z",
  "executionHash": "9f2c…"                // sha256 over every field above
}

The hash is taken over a canonical form with a fixed field order, so the same execution hashes identically on any machine in any language. Change the block number, the summary, the finch id or the approver and verifyProofOfFlight() fails.

A proof is issued only for an execution that actually confirmed. Pending, denied, reverted, or never-simulated actions throw ProofUnavailableError rather than producing a weaker receipt — that refusal is the whole point, since a proof of flight means the flight happened.

issuing and checking one
import { buildProofOfFlight, verifyProofOfFlight } from "@finch/flightpath";

// record is the ExecutionRecord returned by any Flightpath write
const proof = await buildProofOfFlight(record, { nestId: "pons-intelligence", taskId: "t4" });

const { valid, expectedHash } = await verifyProofOfFlight(proof);
// valid === false for any edited field

Model traces and tool logs stay in the execution record offchain; only the 32-byte hash needs anchoring. The network page counts proofs by counting confirmed executions, which is exactly the set for which a proof can be issued.

Publishing — open and free

Anyone can put a finch or a nest in the registry. There is no charge and no token to hold; the only requirement is a publisher key, and a key is issued to any wallet that signs a plain message for one. The signature proves control of the address; the address becomes the key's owner; every listing published with it belongs to that wallet and can only be changed by it. One active key per wallet — signing again replaces the old one.

getting a key and publishing
# 1. the wallet signs a plain message (not a transaction)
Finch publisher key
Address: 0xYourAddress
Nonce: <random, 8-64 url-safe chars>

Signing this issues a key for publishing to the Finch registry. It is not a transaction.

# 2. exchange the signature for a key — shown once, only its hash is stored
POST /api/keys   { "address": "0x…", "nonce": "…", "signature": "0x…" }
→ 201 { "key": "finch_…", "owner": "0x…", "scopes": ["aviary:publish", "nests:write"] }

# 3. publish with it
POST /api/aviary   headers: x-finch-key: finch_…
POST /api/nests    headers: x-finch-key: finch_…     # a nest.manifest/0.1
POST /api/finches  headers: x-finch-key: finch_…     # a finch.manifest/0.1

The gate is a switch, not an inference. PUBLISH_GATE unset or open is the default and the truth right now. hold turns on a $FINCH gate — a publisher must then hold at least PUBLISH_COST_FINCH of the token, read live at publish time — and the publish panel and GET /api/publish/status say so the moment it is on. Setting a token address alone changes nothing.

Published entries carry source: "published"; the network's own analysts carry source: "builtin". Either can be composed into a nest by reference — { handle, ref: "registry" } — and is hydrated from the registry before the strict manifest schema runs.

The hive

Every nest that runs teaches a shared memory, and every finch reads from it. The hive is not a chat log: it accepts only observations with provenance — which run, which nest, which finch, which channel, and the address the finding is about. A finch recalling a prior finding sees it labelled exactly that way, [prior finding · pons-intelligence · 3h ago · unverified], so it can build on it without mistaking it for something it verified itself.

Only the network's builtin nests write to the hive today; published nests read from it. The subject of a finding is the first address in the objective, so a token due-diligence run and a wallet analysis of the same contract meet in the same place. GET /api/hive shows what the hive holds, with the provenance of every line.

User-signed execution

No key on the server ever signs for a visitor. A finch that is allowed to write — wallet.mode: "operator" with allowances — plans and simulates the transaction, then parks it at awaiting_signature with the exact prepared fields. The visitor's own wallet signs it. Nothing on this path turns "the API returned 200" into "the transaction succeeded".

the signed path
# run a finch that is allowed to write, naming the wallet that will sign
POST /api/school/run   { "preset": "courier-finch", "prompt": "send 0.001 ETH to 0x…", "signer": "0xYourAddress" }
→ executions: [{ id: "exec_…", state: "awaiting_signature",
                 prepared: { from, to, value, data, gas } }]

# the wallet signs exactly `prepared`; hand back the hash
POST /api/executions/exec_…/submitted   { "hash": "0x…", "from": "0xYourAddress" }
→ the chain's transaction at that hash is compared to `prepared` field by field:
   to · value · data · from.  Any difference → 422, nothing advances.
→ match: state submitted → confirmed | reverted, with the receipt
→ confirmed: a Proof of Flight is issued

# later, exactly as stored (and self-healing if the receipt arrived late)
GET /api/executions/exec_…

States: created → simulated → awaiting_signature → submitted → confirmed | reverted, and every transition is a compare-and-set, so a double submit cannot double count. The per-transaction cap is enforced when the intent is prepared; the daily allowance is kept durably per signer, so the next intent any instance prepares for that wallet sees what it already spent today. A nest whose policy is not read-only takes the same signer on POST /api/nests/run and surfaces its parked writes next to the task that prepared them.

Explorer tools

Alongside RPC reads, finches have the block explorer. Ten tools read Blockscout's v2 API for Robinhood Chain — chain stats, wallet profiles, transactions and holdings, token profiles, holders, transfers and the token list, single transactions, and contract verification — plus token_pools and pool_state, which find a token's liquidity pools and read a V3 pool's liquidity, price and balances straight from the contract. A tool that finds nothing returns nothing; the finch is told an empty result is the answer, not a prompt to invent one.

Finch's own contracts are verified on the explorer, so anyone can read the source that is actually deployed: FinchRegistry 0x4211…Fb6C, OperatorBudget 0xF61A…01F3, FeeVault 0x20f5…D165, FeeSplitter 0x5819…dB34.

$FINCH

$FINCH is the ERC-20 at 0xFf37F6921eFF863BB1c245A415BB756A8AF08B21 on Robinhood Chain — name Finch Nests, symbol Finch, 18 decimals, total supply 1,000,000,000. It launched on 3 September 2026 at block 53,812,350 through PonsV2LaunchAndBuy 0xe33E…2948 into the PonsV2LaunchFactory 0x7eD5…EC7e (creation tx 0x7270…2e78). Both Pons contracts are verified on the explorer; the token contract's own source is not, and the site says so rather than hiding it.

The factory's record, as read: phase PoolCreated — graduated, with a live Uniswap V4 pool — a creator tax of 300 bps (3%) to 0x55bb1a9F0252d37121F1344e3693B59dD1Ce0389, native ETH as the pair, a 4.2 ETH graduation threshold, pool fee 0 and tick spacing 200. The pool key is (native, token, 0, 200, hook 0xE5e7…e044) and its id is 0xd1fb02ff…1262; price and liquidity are read from the PoolManager with extsload and computed in bigint scaled by 1e18, never through a double.

Every swap on the pool pays a 1% hook fee (hookFeeBps 100), and the 3% creator tax accrues inside the V2MemeHook as pendingCreatorTax(poolId, currency). A sweep credits the creator share to the V2FeeEscrow, from which the recipient claims it; forwarding anything onward from that wallet — the designed FeeSplitter split of 90% prize / 10% ops — is a signed operation, never automatic.

reading it
# from a finch — read-only tools
pons_status      # verified factory, $FINCH's launch record, pool price when initialised, pending creator fees
pons_launch      # { token } → the factory's record for any token, or exists: false — never a guess

# from the site
GET /api/token   # FinchTokenReadout: token · launch · pool · fees · holders · sourceVerified (+ cache, cachedAt)

# from @finch/flightpath
import { readFinchToken, readPonsLaunch, v4PoolKeyFor, v4PoolIdFor, readV4PoolState, readCreatorFees } from "@finch/flightpath";
const readout = await readFinchToken();            // never throws; every sub-read isolated, unreachable stays unreachable
const launch  = await readPonsLaunch(readout.address);

The token gates nothing. Publishing is open and free with PUBLISH_GATE unset, exactly as it was before the launch, and the home page reads that from the same switch the API enforces — see Publishing.

Security model

Custody is simple and layered: the 3% Pons creator tax is claimable only by the recipient recorded in the verified Pons factory — the Finch fee wallet 0x55bb…0389, its key held offline, never in any Finch system — and the FeeVault's only possible destination is that same wallet. The treasury funds a bounded float in OperatorBudget, which enforces per-operator, per-token, per-epoch allowances onchain. Agents hold only restricted operator wallets, and the offchain PolicyEngine mirrors the same limits with recipient/contract allowlists, per-tx caps, human approval thresholds and kill switches. Production deployment is gated on the audit checklist in AUDIT.md — critical findings block release.

Environment reference

variablescopepurpose
GROQ_API_KEYserverFree-tier inference. Any one compute key enables previews and nest runs.
CEREBRAS_API_KEYserverFree-tier alternative; fastest throughput of the hosted options.
OPENROUTER_API_KEYserverFree-tier alternative; :free model variants cost nothing.
GEMINI_API_KEYserverFree-tier alternative via the OpenAI-compatible endpoint.
ENABLE_OLLAMAserverRun inference locally with Ollama — no key, no quota, no per-request cost.
FINCH_PROVIDERserverForce one provider by id. Otherwise Finch prefers free tiers automatically.
HYPERBOLIC_API_KEYserverPaid provider. Used only when no free-tier provider is configured.
MONGODB_URIserverLeast-privilege user, readWrite on the finch db only. Optional — seed fallback without it.
MONGODB_DBserverDatabase name; defaults to finch.
NEXT_PUBLIC_ROBINHOOD_CHAIN_IDpublicOverride only — defaults to 4663, the live Robinhood Chain mainnet id.
NEXT_PUBLIC_ROBINHOOD_RPC_URLpublicOverride only — the mainnet RPC is baked in. Set ROBINHOOD_RPC_URLS for failover endpoints.
NEXT_PUBLIC_ROBINHOOD_EXPLORER_URLpublicBlock explorer base URL.
FLIGHTPATH_OPERATOR_KEYruntime onlyRestricted operator wallet key. Never in the web app, never the treasury.
FINCH_FEE_WALLET_ADDRESSserverCreator-fee recipient address for the $FINCH Pons launch (3% creator tax).
FINCH_FEE_WALLET_PRIVATE_KEYruntime onlyFee-wallet key; readable only by src/server/wallet.ts. Never client-side, never logged.
FINCH_REGISTRY_ADDRESSserverFinchRegistry contract. Until set, listings are reported unregistered rather than implied verified.
FINCH_TOKEN_ADDRESSserverDefaults to the verified $FINCH address (0xFf37…8B21); override only for a redeploy. Setting it gates nothing — PUBLISH_GATE is the switch.
FINCH_FEE_VAULT_ADDRESSserverFeeVault contract (0x20f5…D165). Display only — the creator-fee recipient recorded in the Pons factory is the fee wallet, not the vault.
FINCH_OPERATOR_BUDGET_ADDRESSserverOperatorBudget contract address.
PONS_FACTORY_ADDRESSserverVerified PonsV2LaunchFactory (0x7eD5…EC7e) by default; override only if Pons redeploys.
RWA_APPROVED_ASSETSserverJSON array of approved RWA assets for agent interaction.

Contributing

Protocol-level changes go through Finch Improvement Proposals — one page: motivation, specification, security considerations. See the current set and the process on the research page. Code contributions follow the repository README; the audit checklist applies to anything touching signers, fees, or permissions.