MINARA

Fund-moving confirm

Every tool that moves money is gated preview then execute. Why it exists, what it covers, and the one exception.

You tell the agent: "swap 100 USDC to ETH." The agent doesn't just do it. First you see a confirm card with the trade details:

Sell: 100 USDC (Ethereum)
Buy:  ~0.0286 ETH (estimated, 0.5% slippage)
Route: Uniswap V3
Gas estimate: ~$1.20

You read it, decide it matches what you meant, and accept. Only then does the agent broadcast the transaction.

Why doesn't the agent just do it? Because LLMs misread. "100 USDC" can become "1000 USDC", an address you typed can drift one character, the chain can quietly hop from Ethereum to Base. Once broadcast, none of that is reversible. So every money-moving tool is structurally two-step: preview first, execute second.

The contract

Every fund-moving tool declares controlPolicy.confirm on its ToolEntry. The control-policy gate at the tool-call veto point runs preview, collects consent, and only then passes the call to an execute-only handler. This is enforced by the harness, not by the prompt, and not inside the handler.

Why a hard contract instead of a polite prompt? Because LLMs can "forget" prompt-level rules under prompt injection, sleight-of-hand from a malicious skill, or a hostile tool result that the LLM treats as instruction. The model calls the tool once with the action parameters. Without confirm evidence the gate returns a preview envelope, never a transaction. The client renders the card; the model never sees the preview and there is no second tool call.

The policy lives in tool-control-policy.ts. The hooks that interpret it live in tier-gate.ts. Every new fund-moving tool must declare controlPolicy.confirm; reviewers reject PRs that don't. A fund-moving entry with no declared confirm path fails closed.

Which tools this covers

Tools are marked isFundMoving: true in the tool registry. The list, grouped by purpose:

Token trading

  • swap_tokens — DEX swap across chains
  • buy_token / sell_token — explicit buy / sell side
  • transfer_token — wallet-to-address transfer

Perpetual futures

  • open_perps_position / close_perps_position
  • minara_perps_wallet_sweep — move balance between sub-accounts
  • minara_perps_wallet_transfer — transfer perps balance
  • minara_wallet_fund_perp — fund the perps account from spot (USDC deposit)

Autopilot — turning autonomous trading on or off

  • minara_autopilot_enable / minara_autopilot_disable

The autopilot toggle is fund-moving because flipping it on is the operator authorizing the agent to trade on its own. Once enabled, individual autopilot trades inherit that authorization — they don't re-prompt for every trade.

Workflows

  • workflow_activate / workflow_deactivate

Same idea: activating a workflow is the moment you say "this automation is approved to run on its own."

Strategy studio — your own strategy

  • the strategy-codegen skill's deploy.sh

Deploying is a one-time authorization — see Strategy deployment is a one-time authorization below.

Running another creator's strategy live

  • minara_top_strategies_apply_start — run a featured Top Strategies strategy These start or stop autonomous trading with the user's funds and have no author-time approval moment, so they run the same confirm gate as autopilot (apply_start / sub_start are MANUAL_ONLY; sub_stop is ALWAYS_CONFIRM).

Direct exchange placement

  • All hyperliquid_* place / cancel / withdraw endpoints

If you're adding a new fund-moving tool, mark it isFundMoving: true and declare controlPolicy.confirm. Code review will block the PR if you don't.

The flow in practice

A walkthrough, from the agent's call to your accept:

LLM: swap_tokens({
  sell_token: "USDC",
  sell_amount: 100,
  buy_token: "ETH",
  chain: "ethereum"
})

→ gate: no confirm evidence
→ runs the declared preview (simulate), blocks with a
  minara-confirm envelope
→ client renders the preview card; the model never sees it

You accept on the card

→ the same call proceeds to the execute-only handler
→ handler signs + broadcasts
→ returns {tx_hash: "0x...", executed: true}

A caller-supplied confirm: true still counts as interactive consent evidence. The check is lenient (true, "true", 1, "yes", "on") because some tool-call protocols stringify booleans. The UI path does not depend on the model passing that flag.

Strategy deployment is a one-time authorization

Strategy deployment works differently. The strategy-codegen skill's deploy.sh runs a single two-step confirm at deploy time. Without --confirm it only prints the full deployment preview (symbol, interval, leverage, sub-account, drawdown cap). You see it and decide once, then the same command re-runs with --confirm.

After that, the strategy's lifecycle scripts (start.sh, stop.sh, reconfig.sh) each run the same preview then --confirm two-step, and the stop default market-closes open positions. They're scheduling operations on an already-authorized strategy, not new money movements.

Running another creator's strategy is different: there is no author-time authorization, so the Top Strategies minara_top_strategies_apply_start DOES run the confirm gate, like enabling autopilot.

Why this exception? If every algorithmic trade required a human confirm, the strategy is just a manual trading script. The point of strategy-studio is to delegate within constraints you set upfront. Deploy is the moment you authorize the envelope; after that, the strategy is bounded by drawdown caps, allocation limits, and the symbol allowlist baked into its deployment config, not by a per-trade prompt.

This exception applies only to strategy-studio. Don't try to mimic the pattern elsewhere. The rest of the tool surface uses the unified confirm gate.

MINARA_SKIP_FUND_CONFIRM — the operator-level bypass

There is one escape hatch for non-interactive contexts. The env var MINARA_SKIP_FUND_CONFIRM=1 (or the safety.skipFundConfirm preference) lets the confirm gate treat the call as already evidenced. When to use it, when never to:

Reasonable uses (a handful of legitimate cases):

  • Backtests. The caller is a Python harness; there's no human to read the preview, and the run is sandboxed against historical data anyway.
  • Workflow engine execution. The workflow was already approved at activate time. The execution loop is dispatching on an authorized workflow, not asking the user to re-confirm each step.
  • CI smoke tests. The whole point is to drive the fund-moving path and verify the wire call.

Never use it for:

  • Interactive REPL sessions. This bypasses the last line of defense protecting you.
  • "It's annoying to confirm every time" on a production box. That's turning the safety off.
  • Shell rc files. Don't drop export MINARA_SKIP_FUND_CONFIRM=1 into .zshrc and forget about it. The next time you start the agent it's running unguarded.

The full env-var reference is at Environment variables → MINARA_SKIP_FUND_CONFIRM.

Layer 3 vs Layer 4 — who catches what

The most common confusion: when is the gate Layer 3 (this page) versus Layer 4 (the script-risk gate)? Quick reference:

You ask the agent to…Caught by
"Swap 100 USDC to ETH" — direct trade requestLayer 3 — the control-policy gate previews, then confirms
Run a Python script containing subprocess.run(["minara","swap",…])Layer 4 — script-risk gate sees fund-moving CLI, prompts YELLOW
Run cast send 0x… 1ether from terminalLayer 4 — gate inspects the shell command, prompts YELLOW
Patch a file so it ends up containing minara swapLayer 4 — gate scans the post-apply content, prompts YELLOW
Deploy a strategy via the codegen skill's deploy.shScript-level two-step — preview without --confirm, execute with it; backend risk layer backs it

Short version: Layer 3 catches direct fund-moving tool calls from the agent. Layer 4 catches fund-moving code inside the scripts and commands the agent runs. They cover different parts of the same threat and work in parallel.

Operator checklist

  • ✅ When you see a preview, actually read it. Verify the destination, the chain, the amount, and the slippage match what you asked for. The two-step exists so you can catch your own mistakes too.
  • ✅ When deploying a strategy, audit the deployment config carefully. It's the only time you'll be asked.
  • ✅ When activating a workflow, read the workflow definition end-to-end before clicking yes. Subsequent execution of that workflow won't re-ask.
  • ❌ Don't set MINARA_SKIP_FUND_CONFIRM=1 in an interactive REPL session. Don't put it in .zshrc or .envrc.
  • ❌ Don't accept a preview you haven't read. The confirmation is your judgment, not the agent's.

On this page