Community TypeScript SDK for Star Atlas SAGE

Speak in fleets,
not bytes.

Build Star Atlas apps without becoming an on‑chain program expert. The Kit speaks the language of the game — fleets, cargo, recipes, starbases, movement — instead of accounts, byte arrays, and instruction builders. Read the universe, plan your moves, sign nothing you haven’t seen.

npm install @aephia/sage
Fleet Ravager subwarp
Position
Progress
Arrival

This flight is interpolateSubwarpPosition(state, now) — every frame, on the real Kit. real Kit math · demo state

import {
	deriveFleetTimers,
	getFleet,
	interpolateSubwarpPosition,
} from '@aephia/sage/fleets';

const fleet = await getFleet(ctx, fleetAddress);
const timers = deriveFleetTimers(fleet);

if (fleet.state.kind === 'subwarp') {
	const now = BigInt(Math.floor(Date.now() / 1_000));
	const { position, progress } = interpolateSubwarpPosition(fleet.state, now);
}

Work with the game,
not the blockchain.

You want a fleet’s name — the blockchain gives you bytes. You want its cargo — the blockchain gives you accounts to find, fetch, decode, and join. You want to know whether it’s docked — the answer is buried in raw state. The Kit does that work for you. Names are strings, quantities are exact bigints, state is a union you can switch on, and relationships read like a data model because the Kit knows how every account finds every other one. Underneath, it still drives the generated C4 bindings — all 48 account codecs and 239 instruction builders of them — but no raw shape ever crosses your app’s boundary.

import { address, createSolanaRpc } from '@solana/kit';
import { createSageClient } from '@aephia/sage';

const rpc = createSolanaRpc('https://testnet-rpc.z.ink');
const sage = createSageClient({ cluster: 'zink-ptr', rpc });

const myWallet = address('J4r2s9QA2SHWf8zLPmvPhVphiK92h3rVmoXxcSM8M2vv');
const character = await sage.characters.forProfile(myWallet);
const fleets = await character.fleets.all();
const inventory = await fleets[0].inventory.get();

Keep the blockchain
out of your app.

Every piece of SAGE internals you implement yourself becomes code your app has to maintain. A mining screen shouldn’t need to know how SAGE encodes quantities, where definitions live, or how slots become time. With the Kit, that knowledge stays in the Kit — when Star Atlas ships the next SAGE version, the Kit absorbs the churn, and your app keeps talking about fleets. Less code that says more. A mining status screen, both ways:

Raw bindings

// fetch the Fleet account, decode it
// check the discriminator, unpack the state enum
// fetch the 2.5 MB Game account, decode it
// join resource ids against the definitions block
// convert Floyd-encoded rates by hand
// derive elapsed time from slots yourself
// ...
// repeat after every SAGE upgrade

Star Atlas Kit

const mining = await fleet.mining.get();
const output = mining?.outputs[0];

The chain stores the past.
The Kit renders the present.

A fleet crossing the galaxy doesn’t keep writing its position to the blockchain. The chain records that it left here, is heading there, departed at this time, and will take this long. Where is it now? That’s math — and the Kit ships it as pure functions: state in, now out.

You already watched one: the flight at the top of this page. Here’s another — drop into the belt and watch a hold fill at the chain’s own rate. And because the math is local and deterministic, your UI never has to keep asking an RPC where things are. Fetch once. Let the clock move it.

Mining Carbon asteroid belt
Rate
 u/s
Extracted
Capacity

This progress bar — and the one over the ship above — is projectMiningOutputUnits(rate, elapsed). So is the one in your app. real Kit math · demo state

import {
	deriveMiningStateAgeSeconds,
	getFleetMiningState,
	projectMiningOutputUnits,
} from '@aephia/sage/mining';

const mining = await getFleetMiningState(ctx, fleetAddress);
const output = mining?.outputs[0];
if (mining && output) {
	const now = BigInt(Math.floor(Date.now() / 1_000));
	const elapsed = deriveMiningStateAgeSeconds(mining.lastUpdatedAtUnixSeconds, now);
	const mined = projectMiningOutputUnits(output.unitsPerSecond, elapsed);
}

Your copilot gets it
right the first time.

Ask Claude, Codex, or Copilot to “show the player’s fleets and their cargo”, and generating await character.fleets.all() is a far easier job than reconstructing the right sequence of SAGE accounts, addresses, and codecs. That’s not luck — the Kit is deliberately designed to be easy for humans and coding agents to understand.

Writes you can read
before you sign.

Say you want your fleet to refuel, undock, and begin a warp. The Kit builds those actions into a Plan — and a Plan is just data. Inspect it, combine it, show it to your user, serialize it, or assemble it into one unsigned transaction under an explicit compute and fee policy. The Kit never signs and never submits — your wallet, your final look.

import { combinePlans, assemblePlan } from '@aephia/sage/planning';

const departure = combinePlans(refuel, undock, warp);
const tx = await assemblePlan(ctx, departure, policy);
  1. Refuel Ravager at Starbase MRZ‑12.
  2. Undock from the starbase.
  3. Begin the warp to [12, 4].

3 steps · 1 transaction · unsigned

One plan is one transaction: only steps the chain can execute together, in the same instant. Anything separated by game time — warp cool‑downs, subwarp travel — belongs to a next plan, and deriveFleetTimers tells you when the fleet is ready for it.

Under the hood

Show me more
  • Zero-config clusters The 'zink-ptr' preset knows the program and Game addresses.
  • Smart caching Address-keyed, commitment-aware, seqId-validated definitions.
  • Live data watchFleet() snapshots and replaceable subscription providers.
  • Errors with remedies Every code documented with its producer — written for self-correction.
  • Every example tested Compiled and CI-verified, across the whole public surface.
  • AI-readable docs llms.txt / llms-full.txt.
  • Escape hatch included The generated C4 bindings, re-exported at @aephia/sage/bindings.

This page is the first app
built on the Kit.

The starfield, the flight, the cargo bar, the countdowns — every moving number on this page comes out of @aephia/sage. No mock animation library, no faked easing curves: the same pure functions you’d call in your own tool are running right now, behind this text.

Read the Kit’s source Get it on npm