.000Manual · vol. I

The manual.

A working reference for Eliro. What each instruction does, what it verifies, and how to invoke it from a script. Read in order, or jump to the chapter you need.

.001Preface

Preface.

Eliro is a treasury operating system for autonomous agents, built as a single Solana program. Capital will be held in a program-derived address. The reserve. Each agent is given a lane within that reserve, with its own balance, ledger, and constraint envelope.

The system replaces the server-side budget. Where most stacks ask you to trust an operator with rules and balances, Eliro stores the rules on-chain and verifies them in the same instruction that moves the funds. There is no middleware. There is no key handed to the agent.

Posture. The chain holds the capital. The program holds the rules. The agent only signs intent. Never authority.

.002Architecture

Architecture.

The system is composed of four on-chain elements:

  • ProgramAnchor 0.32.1. The contract logic.
  • ReservePDA holding all capital. USDC by default.
  • LanePDA per agent. Derived from reserve + label.
  • Account dataConstraints, roster, hours. Stored per lane.

The authority is the wallet that deployed the reserve. Only the authority can create lanes, set constraints, or move the roster. The agent itself only invokes payment instructions. Authority over the reserve is never transferred to it.

.003Settlement

Settlement.

When an agent asks to pay, it does not transfer directly. It invokes the reserve program with an intent: a lane, an amount, and a destination. The program runs every check in a single instruction:

  • StatusThe lane is active. Not paused, not closed.
  • AmountWithin per-tx maximum and rolling daily cap.
  • HoursInside an allowed window (if configured).
  • RosterRecipient is on the lane's allowlist.
  • BalanceLane has sufficient funds.

If any check fails, the entire transaction reverts. Nothing moves. If all pass, the program signs the transfer from the reserve to the recipient and writes the new balance, spent count, and transaction count to the lane.

.004The Reserve

The Reserve.

The reserve is the root account. It is created once and lives for the lifetime of the system. The PDA is derived from the authority key plus a small set of seeds, so the address is deterministic and computable off-chain.

create_reserve.rseliro · src
pub fn create_reserve(
    ctx: Context<CreateReserve>,
    name: String,
    initial_deposit: u64,
) -> Result<()> {
    let reserve = &mut ctx.accounts.reserve;
    reserve.authority = ctx.accounts.authority.key();
    reserve.name = name;
    reserve.balance = initial_deposit;
    reserve.created_at = Clock::get()?.unix_timestamp;
    reserve.bump = ctx.bumps.reserve;
    Ok(())
}
TypeScript · Eliro SDKeliro · src
import { Eliro } from "@eliro/sdk";

const eliro = await Eliro.connect(connection, wallet);
const reserve = await eliro.deployReserve({
  name: "production",
  initialDeposit: 10_000n * 10n ** 6n,
});
console.log("Reserve:", reserve.address.toBase58());
.005Lanes

Lanes.

Each agent gets a lane. A partition within the reserve. Lanes are PDAs derived from the reserve and a label. They are deterministic; their addresses can be computed from the label alone.

A lane carries balance, spent count, transaction count, and a status flag. Authority can create, pause, resume, or close a lane at any time. A closed lane cannot be reopened.

open_lane · TypeScripteliro · src
const lane = await reserve.openLane({
  agentId: "research_agent",
  budget: 2_000n * 10n ** 6n, // 2,000 USDC
});

// Compute the lane address off-chain
const address = Eliro.deriveLane(reserve.address, "research_agent");
.006Governance

Governance.

Governance is the constraint envelope on a lane. It defines spending discipline as account data:

  • max_per_txMaximum allowed for a single transaction.
  • max_per_dayRolling 24-hour cap. Resets via Solana clock.
  • lifetime_capOptional total ceiling that never resets.
enforce. Execute_payment fragmenteliro · src
require!(amount <= rules.max_per_tx, ElError::ExceedsMaxPerTx);

let elapsed = clock.unix_timestamp - rules.day_start;
let spent = if elapsed >= 86_400 { 0 } else { rules.spent_today };
require!(
    spent + amount <= rules.max_per_day,
    ElError::ExceedsDailyCap
);
.007Roster

Roster.

The roster is the per-lane allowlist of recipient addresses. Each entry is a small PDA. Address plus label plus the timestamp it was added. The program checks every outbound payment against the lane's roster before signing.

add_to_rostereliro · src
const ROSTER = [
  { address: "4zMMC9...openai", label: "OpenAI · API" },
  { address: "7xKm3...claude", label: "Anthropic · Claude" },
  { address: "9pRt7...market", label: "Feed · Market data" },
];

for (const entry of ROSTER) {
  await lane.addToRoster(entry);
}

A compromised agent cannot exfiltrate to an unapproved address. The program will not sign the transfer. Period.

.008Hours

Hours.

Hours bound when a lane may move funds. Define windows in UTC, with an optional day-of-week filter. Outside any defined window, transactions are rejected. The Solana clock sysvar is the time source. The agent's server clock is irrelevant.

set_hourseliro · src
await lane.setHours({
  windows: [
    { startHour: 9, endHour: 17, days: [1, 2, 3, 4, 5] }, // Mon–Fri
  ],
});
.009Replenish

Replenish.

Replenish is the auto-refill instruction. Set a floor and a target on a lane. When the lane's balance drops below the floor, anyone can crank the instruction. But only the program can move the funds, and only up to the target.

configure_replenisheliro · src
await lane.configureReplenish({
  floor:  500n * 10n ** 6n,    // refill when below 500 USDC
  target: 2_000n * 10n ** 6n,  // refill up to 2,000 USDC
  enabled: true,
});

// Permissionless crank. Anyone can call this
await reserve.crankReplenish(lane.address);
.010Quorum

Quorum.

Quorum is the multi-signer rule. Above a defined amount, a payment becomes a proposal account on-chain. Each required signer approves the proposal; once the threshold is met, anyone may execute. Proposals expire after a configurable TTL. Signer sets are stored on-chain and rotated by the authority.

quorum floweliro · src
await reserve.configureQuorum({
  signers: [w1.publicKey, w2.publicKey, w3.publicKey],
  threshold: 2,                 // 2-of-3
  amountThreshold: 5_000n * 10n ** 6n,
  proposalTtl: 86_400,          // 24h
});

const proposal = await reserve.createProposal({
  destination: vendor,
  amount: 10_000n * 10n ** 6n,
});

await proposal.approve(w1);
await proposal.approve(w2); // threshold met
await proposal.execute();
.011Interface

Interface.

The TypeScript SDK provides a typed interface over the program. It handles PDA derivation, transaction construction, confirmation, and account decoding.

MethodAction
Eliro.connect()Bind connection + wallet.
eliro.deployReserve()Create the reserve PDA.
reserve.openLane()Open a per-agent lane.
lane.setGovernance()Set max-per-tx + daily cap.
lane.addToRoster()Add an approved recipient.
lane.setHours()Configure time windows.
lane.configureReplenish()Set auto-refill envelope.
reserve.configureQuorum()Configure m-of-n quorum.
reserve.createProposal()Open a quorum proposal.
proposal.approve() / execute()Submit and finalise.

The TypeScript SDK is in private preview. The signatures here reflect the target API. Reach out for early access.

install · plannedeliro · src
npm install @eliro/sdk @solana/web3.js