---
name: aephia-atlas-kit
description: Read and change Star Atlas SAGE C4 game state from TypeScript with the @aephia/atlas-kit SDK - fleets, cargo, mining, crafting, markets, claim stakes, factions, loyalty, and inspectable transaction Plans. Use when writing code against Star Atlas SAGE, the @aephia/atlas-kit package, or the z.ink test realm.
---

# Atlas Kit

`@aephia/atlas-kit` is an unofficial `0.x` TypeScript SDK for Star Atlas SAGE C4. It
validates every account it reads - owner, discriminator, shape, and
relationships - and exposes gameplay snapshots rather than raw account layouts.
Writes are built as inert, inspectable Plans that only execute when a caller
explicitly configures execution.

```bash
npm install @aephia/atlas-kit
```

## Getting the shape right first

These are the points where a reasonable guess produces broken code.

- **ESM only.** The package is `"type": "module"` with no CommonJS build.
  `require('@aephia/atlas-kit')` will not work.
- **Addresses are `@solana/kit` `Address` values**, which are branded strings.
  This is not `@solana/web3.js`; there is no `PublicKey` class and no
  `.toBase58()`.
- **Wallet to Profile is a search, not a calculation.** A Profile address is
  assigned at creation and nothing about the wallet predicts it, so there is no
  PDA to derive. The SDK resolves it but never scans the chain to do so - you
  choose how it searches. Either configure `discovery: { walletProfiles }` with
  an indexer or mapping you supply and use `strategy: 'provider'`, or pass
  `{ strategy: 'known-addresses', addresses }` when you already hold candidates.
  Either way the SDK validates each candidate's key list before returning it,
  and a wallet may hold zero, one, or many Profiles. This is the single most
  common place to get stuck; if you control the Profile addresses, skip it and
  call `sage.characters.forProfile(profileAddress)`.
- **Raw quantities are `bigint`.** Fields suffixed `Raw` (`quantityRaw`,
  `capacityRaw`) keep exact values. Do not cast them to `number` for arithmetic.
- **Never reach for previous-generation packages.** `@staratlas/sage`,
  `@staratlas/data-source`, broad `readAllFromRPC` scans, and ad hoc Anchor
  recipes target an older game generation. Pinned `@staratlas/dev-*` bindings
  are reachable only through `@aephia/atlas-kit/bindings`.
- **CargoPods are nested values, not addressable accounts.** Read fleet cargo
  through `fleet.inventory.get()` or `getFleetCargoInventory` from
  `@aephia/atlas-kit/cargo`.
- **Dispose the client** when you are done, so caches and subscriptions release.

## Two ways in

Use the root client for gameplay traversal, and capability entries when you want
a small bundle.

Most sessions begin with a wallet. Discovery needs a provider you supply,
because the SDK will not scan the chain on your behalf.

```ts
import { createSageClient, type Address } from '@aephia/atlas-kit';
import type {
  SageRpc,
  WalletProfileDiscoveryProvider,
} from '@aephia/atlas-kit/client';

declare const walletAddress: Address;
declare const rpc: SageRpc;
// Yours to provide: an indexer, or a mapping you maintain.
declare const walletProfiles: WalletProfileDiscoveryProvider;

const sage = createSageClient({
  cluster: 'zink-ptr',
  rpc,
  discovery: { walletProfiles },
});
try {
  const wallet = sage.wallets.get(walletAddress);
  const characters = await wallet.characters.all({ strategy: 'provider' });
  for (const character of characters) {
    const fleets = await character.fleets.all();
    console.log(
      character.address,
      fleets.map(({ name }) => name),
    );
  }
} finally {
  await sage.dispose();
}
```

With no provider configured, `strategy: 'provider'` throws and says so. Given a
Profile address already, `sage.characters.forProfile(profileAddress)` skips
discovery entirely.

## Entry points

Entry points follow gameplay capabilities, not individual accounts.

| Entry point                      | Purpose                                                                                             |
| -------------------------------- | --------------------------------------------------------------------------------------------------- |
| `@aephia/atlas-kit`                   | Loaded identity, Fleet, cargo, Claim Stake, Starbase, mining, crafting, market, and world traversal |
| `@aephia/atlas-kit/client`            | Context, cache, freshness options, subscriptions, provenance                                        |
| `@aephia/atlas-kit/identity`          | Wallets, Profiles, Characters, XP progression, Game research catalog                                |
| `@aephia/atlas-kit/world`             | Game, regions, Star Systems, Celestial Bodies, shared Starbase data                                 |
| `@aephia/atlas-kit/starbases`         | Player-local Starbase state, facilities, upgrades                                                   |
| `@aephia/atlas-kit/starbases/actions` | Address-only StarbasePlayer registration planning                                                   |
| `@aephia/atlas-kit/fleets`            | Fleets, ships, composition, movement state                                                          |
| `@aephia/atlas-kit/fleets/actions`    | Fleet undock, coordinate-movement, lane-warp, and docking planners                                  |
| `@aephia/atlas-kit/cargo`             | Cargo pods, inventory, capacity, resource movement                                                  |
| `@aephia/atlas-kit/cargo/actions`     | Fleet/player-Starbase and within-Fleet cargo transfer planning                                      |
| `@aephia/atlas-kit/claim-stakes`      | Claim Stake discovery, ownership, placement, harvesting                                             |
| `@aephia/atlas-kit/mining`            | Deposits, fleet mining, extraction, timing, cargo outputs                                           |
| `@aephia/atlas-kit/planning`          | Inert Plans, persistence, composition, unsigned assembly                                            |
| `@aephia/atlas-kit/crafting`          | Recipes, Crafting Habs, Crafting Processes, production state                                        |
| `@aephia/atlas-kit/combat`            | Combat configuration, Fleet combat state, Loot, Outlaw Flags                                        |
| `@aephia/atlas-kit/scanning`          | Scan patterns, cooldown and stat projections, Character scanning                                    |
| `@aephia/atlas-kit/rewards`           | ATLAS reward epochs, configuration, treasuries, Loot commitments                                    |
| `@aephia/atlas-kit/factions`          | Faction identity, economics, diplomacy, standing, territory                                         |
| `@aephia/atlas-kit/loyalty`           | Faction epochs, Profile contributions, accumulated ATLAS                                            |
| `@aephia/atlas-kit/markets`           | Local and faction markets, orders, maker state, discovery                                           |
| `@aephia/atlas-kit/bindings`          | Raw generated C4 clients, unchanged (escape hatch)                                                  |

Combat, scanning, rewards, factions, and loyalty are stable capability entries
that the root client does not compose - import them directly.

## Writing to the game

A planner returns an inert `Plan`: data describing instructions, requirements,
and a human-readable summary. Nothing signs or submits until `executePlan` is
called with an explicitly configured `SageWriteRpc` and external Kit signers.

Show the caller what a Plan does before executing it. Plans compose
deterministically, and can be simulated with a fee-payer address alone.

## Errors worth handling

- `ACCOUNT_NOT_FOUND` - verify the address and cluster.
- `MISSING_GAME_CONTEXT` - pass `game` when using a custom cluster.
- `INVALID_ENTITY_ID` - ids are 0-65535; Faction and Region ids start at 1.
- `REGISTRY_OUT_OF_SYNC` - refresh and retry; never invent a missing definition.
- `INVALID_ACCOUNT_OWNER`, `INVALID_DISCRIMINATOR`, `ACCOUNT_DECODE_FAILED` -
  stop trusting that account and check the installed binding versions.
- `status: 'unknown'` or `TRANSACTION_OUTCOME_UNKNOWN` - submission may have
  happened. Keep the signature, inspect chain history, reconcile the intended
  action, and **never retry automatically**.

## When the SDK looks wrong

Most apparent SDK bugs are something else: PTR game state that moved since the
data was recorded, an account that genuinely does not exist, RPC flakiness or
rate limiting, a previous-generation `@staratlas` package, or API misuse. Rule
those out first.

Then run the one test that discriminates: read the same account through
`@aephia/atlas-kit/bindings` and compare. If the raw decode is correct and the
translated surface is wrong, it is an SDK bug. If the raw decode is wrong too,
it is upstream or game state - do not file it.

If it survives that, search the existing issues, then **ask the user before
filing anything** and show them the exact title and body first. Standing
approval counts if they have already given it for the session. File against
`Aephia/atlas-kit` using the `SDK bug` form, which lists what to include; the
form applies the `agent:intake` label so the report reaches triage. Without
access to that repository - it is private - write the same report out for the
user to file themselves.

Redact the RPC endpoint: provider URLs routinely embed an API key. Never put a
connection string, a keypair, or any other secret in an issue. Do include the
account addresses and the slot they were read at, from `readMeta`, because that
is what lets the report become a regression fixture once chain state moves on.

## Reading further

Fetch these when a task needs more than the above.

- Everything in one file: https://atlas-kit-docs.pages.dev/llms-full.txt
- Index of the documentation: https://atlas-kit-docs.pages.dev/llms.txt
- Guides, one per capability: https://atlas-kit-docs.pages.dev/guides/
- Generated API reference: https://atlas-kit-docs.pages.dev/reference/
- Star Atlas terms: https://atlas-kit-docs.pages.dev/glossary/
