Your first read
By the end of this page you will have read a real fleet out of the live game. No wallet, no keys, no setup beyond an install.
Install
Section titled “Install”pnpm add @aephia/atlas-kit @solana/kit@solana/kit is a peer dependency: it provides the RPC client and the Address
type. Installing it yourself means you control its version.
Read a fleet
Section titled “Read a fleet”import { createSolanaRpc, address } from '@solana/kit';import { createSageClient } from '@aephia/atlas-kit';
const rpc = createSolanaRpc('https://testnet-rpc.z.ink');const sage = createSageClient({ cluster: 'zink-ptr', rpc });
const profileAddress = address('J4r2s9QA2SHWf8zLPmvPhVphiK92h3rVmoXxcSM8M2vv');const character = await sage.characters.forProfile(profileAddress);const fleets = await character.fleets.all();
const fleet = fleets[0];if (!fleet) throw new Error('That profile has no fleets.');
console.log(fleet.name); // "Ravager"console.log(fleet.state); // { kind: 'docked', system: … }That address is a real profile on the test realm. It should work as written.
In your own application that address is the thing you have to supply. If your users arrive holding a wallet and nothing else, getting from one to the other is a search rather than a calculation — nothing about a wallet predicts its profile, so there is no offline answer. Identity covers how that search works and what you have to bring to it; it is worth reading before you design a sign-in flow.
Try it here
Section titled “Try it here”You do not have to leave the page. Press Run and this executes against the live test realm, in your browser. Change the address to read a different profile.
The documentation playground remains read-only: it uses only read capability entries, with no wallet or signing surface. The SDK itself is non-custodial and can execute transactions when you explicitly supply a write transport and external Kit signers to a write call.
What just happened
Section titled “What just happened”Four things, worth naming because the rest of the SDK works the same way.
createSageClient did no network work. It is synchronous and allocates a
context: a cache, an RPC binding, and the cluster’s known addresses. Nothing is
fetched until you ask for something.
cluster: 'zink-ptr' filled in the game. The preset knows the program
address and the Game account, so you did not have to.
forProfile returned a loaded object. Not a handle you have to load()
first — the data is already there. Every read in this SDK works that way.
.fleets.all() followed a relationship. Going from a character to its
fleets is a separate read, so it is a separate await. Anything that costs a
network round trip is a method you call, not a property you touch.
Reading a bit more
Section titled “Reading a bit more”Cargo is a relationship too:
const inventory = await fleet.inventory.get();console.log(inventory.cargoHold.items[0]?.quantityRaw);Quantities are bigint, not number. Game amounts routinely exceed what a
JavaScript number can hold exactly, and silently rounding someone’s ore count
is worse than making you type n.
Both examples on this page share one client, so the second reuses what the first already fetched rather than reading the same accounts again.
If it did not work
Section titled “If it did not work”A network or CORS error usually means the RPC endpoint rejected the request. See setting up your RPC.
MissingGameContextError means a cluster other than zink-ptr was used
without supplying a Game address. Use the preset unless you know you need
otherwise.
An empty fleets array is a valid answer: that profile currently has no
fleets. Try another address.
Where to go next
Section titled “Where to go next”How the SDK thinks explains caching, entry points, and why reads are shaped the way they are.
If your users will arrive with a wallet rather than a profile address, read identity first. Wallet-to-profile discovery is the one step the SDK cannot answer by itself, and it shapes everything downstream of your sign-in.