Skip to content
Next — unreleased

Starbases

View as Markdown

A starbase is where fleets dock, cargo is stored, and crafting happens. The SDK splits it in two, and knowing which half you want saves confusion.

  • The starbase itself — where it is, what level it is — is shared infrastructure and belongs to world.
  • Your state at that starbase — your cargo, your escrowed ships, your upgrades — is yours, and lives here.
const bases = await character.starbases.all();

Or directly, when you know the system:

const base = await sage.starbases.forCharacterAtSystem(
characterAddress,
systemAddress,
);

A character can have state at many starbases, or none. A character who has never docked anywhere returns an empty array, which is a valid answer rather than a failed read.

Cargo. What you have stored there, as StarbaseCargoInventory. This is distinct from what a fleet is carrying — cargo moves between the two, and reading one tells you nothing about the other.

Escrowed ships. Ships you own that are held at the starbase rather than assigned to a fleet.

Respawn state. Where destroyed ships come back.

Upgrades in progress. Starbase upgrades take real time, so an upgrade is a process with a lifecycle rather than an instant change:

const upgrades = await base.upgrades.all();

Your state at a starbase does not appear on its own: before a starbase can hold anything of yours, you register there once. That is a write — if it would be your first, read How Atlas Kit changes the game before continuing.

planRegisterStarbasePlayer plans that one-time registration for a system with a starbase where you have no state yet:

import { planRegisterStarbasePlayer } from '@aephia/atlas-kit/starbases/actions';
import { getStarSystemById } from '@aephia/atlas-kit/world';
const registered = new Set(
(await character.starbases.all()).map((state) => state.system.address),
);
for (const connection of system.connections) {
const destination = await getStarSystemById(ctx, connection.systemId);
if (destination.starbase === undefined) continue;
if (registered.has(destination.address)) continue;
const plan = await planRegisterStarbasePlayer(ctx, character, destination, {
funder: walletAddress,
});
console.table(plan.describe());
break;
}

The funder is the address that pays the small one-time creation cost; using the wallet that will later sign is the convenient choice, but the planner does not require it. The planner refuses a system without a shared starbase, and refuses to register where your state already exists — registration is once per starbase, which is exactly why the snippet checks what you already have first.

The resulting Plan signs and executes like every other; see Warp & subwarp for that flow.

Starbase cargo and fleet cargo are different accounts. Loading a fleet does not change what the starbase holds, and vice versa. If you are tracking totals, you need both.

A starbase you have never used has no player state. The starbase exists in the world; your state at it does not. That is why the read can legitimately come back empty.

Upgrade timing is derived. Completion times come from the process state plus the Game account’s definitions, not from a stored timestamp you can read directly.