Skip to content
Next — unreleased

How Atlas Kit changes the game

View as Markdown

How Atlas Kit reads the game covers the safe half: reading never changes anything. This page is its counterpart for the calls that do — moving a fleet, transferring cargo, placing a market order. Five rules govern every one of them. Read this once before your first write; every guide that plans an action assumes it.

The client from your first read was created with only a read connection. Planning works with that alone, but rehearsing and executing need a connection that can also submit — passed explicitly as writeRpc, and it can be the same endpoint:

import { createSolanaRpc } from '@solana/kit';
import { createSageClient } from '@aephia/atlas-kit';
const rpc = createSolanaRpc('https://testnet-rpc.z.ink');
const sage = createSageClient({ cluster: 'zink-ptr', rpc, writeRpc: rpc });
const ctx = sage.context;

Leaving writeRpc out is itself a safety boundary: a context without one can never submit anything, and a rehearsal or execution attempted against it fails with a typed MISSING_WRITE_TRANSPORT error instead of quietly picking a transport for you. The snippets below assume a context created as above.

Every change starts with a Plan. A planner reads current game state, validates what it can prove, and returns a Plan object describing exactly one transaction. Creating a Plan sends nothing, signs nothing, and costs nothing — it is still just reading.

import type { PlanAuthorization } from '@aephia/atlas-kit/fleets/actions';
import { planFleetUndock } from '@aephia/atlas-kit/fleets/actions';
const authorization = {
profile: fleet.ownerProfile.address,
authority: walletAddress,
keyIndex: 0,
} satisfies PlanAuthorization;
const plan = await planFleetUndock(ctx, fleet, { authorization });
console.log(plan.summary);

Notice the authorization is made of addresses: it names which Profile and which of its keys will authorize the change later, without involving anything that can sign. One Plan is one transaction, always. There is no call that quietly performs several changes, chooses a route for you, or retries something on your behalf. If a task needs several transactions, that is a sequence of Plans — rule 5.

2. Every Plan can be read before anyone signs

Section titled “2. Every Plan can be read before anyone signs”

A Plan describes itself in game language:

import type { PlanAuthorization } from '@aephia/atlas-kit/fleets/actions';
import { planFleetUndock } from '@aephia/atlas-kit/fleets/actions';
const authorization = {
profile: fleet.ownerProfile.address,
authority: walletAddress,
keyIndex: 0,
} satisfies PlanAuthorization;
const plan = await planFleetUndock(ctx, fleet, { authorization });
console.table(plan.describe());

describe() returns stable, human-readable steps. Show them to the person who is about to sign, every time — approval of an idea is not approval of a transaction. If game state has changed since the Plan was created, make a new Plan rather than repairing the old one.

simulatePlan runs the unsigned transaction against the realm and reports what would happen — logs, compute cost, errors — without ever touching a signer:

import type { PlanAuthorization } from '@aephia/atlas-kit/fleets/actions';
import { planFleetUndock } from '@aephia/atlas-kit/fleets/actions';
import { simulatePlan } from '@aephia/atlas-kit/planning';
const authorization = {
profile: fleet.ownerProfile.address,
authority: walletAddress,
keyIndex: 0,
} satisfies PlanAuthorization;
const plan = await planFleetUndock(ctx, fleet, { authorization });
const rehearsal = await simulatePlan(ctx, plan, { feePayer: walletAddress });
console.log(rehearsal.unitsConsumed, rehearsal.logs.length);

The fee payer here is just an address, like the authority in the Plan — nothing on this page can sign, so no wallet opens and nothing is charged. A failed simulation is a typed error with the realm’s own logs attached, which is a far better place to discover a problem than after signing.

The Kit is non-custodial. It never asks for a seed phrase, never stores a key, and never signs on its own. At the moment of execution it receives a standard signer interface from your wallet integration, uses it for exactly one transaction, and holds no reference afterwards. Which wallet, and how the signing prompt looks, is your application’s decision — the SDK only defines the boundary.

That boundary is also why planning and signing are separate moments: everything before the signer is safe to run anywhere, including in a browser tab that has no wallet at all.

5. Execution happens once, then you read again

Section titled “5. Execution happens once, then you read again”

executePlan checks that the supplied signer matches the Plan, verifies the Plan is still fresh, submits exactly once, and waits for a terminal result. Every executed transaction costs real fees, so nothing is resubmitted quietly.

  • A confirmed result means the change happened. Read the affected state again — the next ordinary read is fresh.
  • An unknown result means submission could not be confirmed either way. Do not retry. Check wallet and chain history first; retrying an unknown outcome is how a fleet gets moved twice.

The full sign-and-execute flow, with code, is in Warp & subwarp — the same flow applies to every Plan in the SDK.

For a journey that needs several confirmed transactions — move, wait, move again — use a Plan sequence. It keeps each step a single transaction, checkpoints progress in storage you own, and requires fresh authorization for every reached step.

Write planning lives in the guide for the concept it changes, next to the reads it depends on:

  • Warp & subwarp — moving a fleet
  • Cargo — transferring cargo
  • Starbases — registering at a starbase
  • Crafting — starting, completing, and cancelling a process
  • Markets — placing a local market order
  • Claim stakes — placing a Claim Stake and its bundled hub
  • Plans — chaining transactions into a journey