---
name: aephia-atlas-kit
description: Build TypeScript consumers of @aephia/atlas-kit for Star Atlas SAGE C4, using validated gameplay reads and inspectable action Plans with explicit simulation and execution on 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@next
```

## 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/identity/actions`     | Profile creation and Character registration with explicit funding                                                                           |
| `@aephia/atlas-kit/world`                | Game, regions, Star Systems, Celestial Bodies, shared Starbase data, derived Starbase status and Planet Claim Stake plot availability         |
| `@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 movement, docking, completed-subwarp settlement, early stop, wallet ship import, empty Fleet creation, and Docked-Fleet ship assembly |
| `@aephia/atlas-kit/cargo`                | Cargo pods, inventory, capacity, resource movement                                                                                          |
| `@aephia/atlas-kit/cargo/actions`        | Fleet/player-Starbase and within-Fleet transfers, wallet deposits and withdrawals                                                           |
| `@aephia/atlas-kit/claim-stakes`         | Claim Stake discovery, ownership, placement, harvesting                                                                                     |
| `@aephia/atlas-kit/claim-stakes/actions` | Claim Stake placement, building changes/finalization/cancellation, rent top-up, deconstruction, and Fleet-transfer start/recovery           |
| `@aephia/atlas-kit/mining`               | Deposits, fleet mining, extraction, timing, cargo outputs                                                                                   |
| `@aephia/atlas-kit/mining/actions`       | Fleet mining start for selected Asteroid resources and mining stop                                                                          |
| `@aephia/atlas-kit/planning`             | Inert Plans, persistence, composition, sequences, unsigned assembly, signer-free simulation, explicit execution and reconciliation          |
| `@aephia/atlas-kit/crafting`             | Recipes, Crafting Habs, Crafting Processes, production state                                                                                |
| `@aephia/atlas-kit/crafting/actions`     | Crafting Process start, completion, and cancellation                                                                                        |
| `@aephia/atlas-kit/combat`               | Combat configuration, Fleet combat state, Loot, Outlaw Flags                                                                                |
| `@aephia/atlas-kit/combat/actions`       | Combat stimulants, singleton Fleet/Starbase attacks, and Loot retrieval                                                                     |
| `@aephia/atlas-kit/scanning`             | Scan patterns, Fleet signals, pattern policies, regional loot and Fleet/Character scanning                                                                            |
| `@aephia/atlas-kit/scanning/actions` | Explicit signal setup and Idle detection |
| `@aephia/atlas-kit/rewards`              | ATLAS reward epochs, configuration, treasuries, Loot commitments                                                                            |
| `@aephia/atlas-kit/factions`             | Faction identity, economics, diplomacy, standing, territory                                                                                 |
| `@aephia/atlas-kit/factions/actions`     | Address-only first faction enrollment planning for an existing Profile                                                                      |
| `@aephia/atlas-kit/loyalty`              | Faction epochs, Profile contributions, accumulated ATLAS                                                                                    |
| `@aephia/atlas-kit/loyalty/actions`      | Address-only full-balance Loyalty ATLAS claim planning                                                                                      |
| `@aephia/atlas-kit/markets`              | Local and faction markets, orders, maker state, discovery                                                                                   |
| `@aephia/atlas-kit/markets/actions`      | Local Market order placement, cancellation/filled-escrow withdrawal, and directional Faction Market trades                                  |
| `@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.

For ordinary docked crew transfers, use `planLoadFleetCrew` or
`planUnloadFleetCrew` from `fleets/actions`, with explicit `count` and direct
`authorization`. Existing Fleet/StarbasePlayer reads expose the counts. Unloading
to zero while Docked is allowed; replenish departure crew before undocking.
Plans stand alone: confirm and refetch before the next action. Do not invent
roster, renting, import or crafting-assignment support from these planners.

## 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.

Wallet cargo uses `planDepositCargoToGame` and `planWithdrawCargoFromGame` from
`cargo/actions`. Deposit takes a separate token `owner`; withdrawal requires an
explicit `recipient` who does not sign just to receive. Withdrawal defaults to
an existing ATA; `destinationAccount: { mode: 'create-if-missing', payer }`
permits canonical ATA creation with explicit storage funding. Existing custom
`source`/`destination` accounts must match the selected owner/recipient and
mint. Use exact raw bigint amounts. Withdrawal currently requires zero-decimal
original Token mints; do not infer remaining epoch allowance or decimal-based
game conversion. Simulate, execute explicitly, and verify confirmed balances.

For wallet ship imports, use `planDepositShipsToGame` from `fleets/actions` with
a loaded CSS Starbase Player, explicit `shipId`, positive raw bigint `amount`,
token `owner` and independent Profile `authorization`. Select by the
configuration's mint in this deployment; do not infer a default configuration.
The original Token source defaults to the owner's ATA, with an existing
validated `source` override supported. Escrow is the Character's ATA and must
exist unless `escrowAccount: { mode: 'create-if-missing', payer }` explicitly
permits creation. The payer signs only when creation is included. A new
configuration entry in Starbase Player uses Profile-funded storage; repeating an
existing entry does not. Importing creates no Fleet. Simulate, execute once and
verify source debit, Character token credit and selected `shipsInEscrow` credit
together. Unknown submission outcomes must be investigated before retrying.

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

A Fleet can retain its stored movement state after travel finishes. Docking and
movement planners accept stored warp/subwarp destinations and rely on chain
simulation to reject travel that has not arrived; undocking requires Docked.

Standalone `planFleetSettleArrival(ctx, fleet, { authorization })` supports only
completed Game version 2 subwarp. Select a direct key of the owning Profile;
canonical Career XP accounts must already exist and be reconciled. Certificates
and delegated sub-Profiles are unsupported. Planning and execution read fresh
chain Clock and journey/authority state, rejecting unfinished or changed
journeys before signing. These checks cannot prevent a concurrent transaction
from starting a new trip afterward: execution could stop that newer trip.
Intentional early stopping is the separate `planFleetStopSubwarp` action.
Standalone warp settlement is unsupported.
Elapsed settlement remains available with insufficient Fuel; it can leave the
Fleet Idle short of its destination. Inspect confirmed state and location, not
just simulation/execution success, and ensure enough Fuel before departure.

For mining after subwarp, settle in a separate confirmed transaction and reload
the Fleet as Idle before preparing `planFleetStartMining`. Mining never inserts
settlement. Current-runtime settle-plus-mine composition is not qualified; do
not recommend it from historical warp evidence or infer arrival from local time.
Inspect and simulate the exact Plan before signing. See the current
[movement guide](https://develop.atlas-kit-docs.pages.dev/guides/moving-a-fleet/#arriving).

Save Plans with `plan.toJSON()` and load them with `planFromJSON`. Recreate
unsupported saved Plans through the current planner; never change version
numbers, remove guards, or restore obsolete APIs.
Not every generated instruction has a curated planner. Use the raw bindings
escape hatch explicitly for unsupported actions; it provides no SDK safety
guarantee.

Use `PlanSequence` when a journey requires separately confirmed transactions.
Its itinerary is non-atomic and does not authorize future Plans: a capability
returns `waiting` or freshly prepares one Plan only when reached, and every step
needs fresh authorization through `onBeforeSign`. Persist caller-owned
checkpoints, never signers, transactions, wallet callbacks, or secrets. Do not
blindly retry or automatically resume an unknown, identity-mismatched, or
confirmation-unverifiable checkpoint. An observable public signature can be
reconciled after chain confirmation; an interrupted opaque `invoking` attempt
has no signature and stays unresolved.

## 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://develop.atlas-kit-docs.pages.dev/llms-full.txt
- Index of the documentation: https://develop.atlas-kit-docs.pages.dev/llms.txt
- Guides, one per capability: https://develop.atlas-kit-docs.pages.dev/guides/
- Generated API reference: https://develop.atlas-kit-docs.pages.dev/reference/
- Star Atlas terms: https://develop.atlas-kit-docs.pages.dev/glossary/

For current scanning state, use `maybeGetScanSignalForFleet(ctx, fleetAddress)`,
`getScanPatternPolicy(ctx, patternId)` and `getScanLootProfile(ctx, regionId)`.
Required/optional forms are available for each. They inspect current accounts;
do not treat projected loot rows as complete eligibility or reward forecasts.
Use `deriveScanLootOdds` with explicit research/region requirements and
exhaustion for labelled client-derived selection estimates; use
`deriveScanExhaustion` with the Fleet's signal and explicit time for the latter.
For pending scans, `deriveCommittedScanLootOdds` uses committed eligibility and
requires matching configuration revisions/fingerprints. Check result status;
unavailable is not zero. These helpers do not establish launch readiness or
promise quantity/recovery; see the scanning guide.
Only Contact/Recovered signals resolve cargo. Exact timestamps and quantities
remain bigint; coordinate/radius values retain I8F56 raw storage.

Use `planOpenScanSignal(ctx, fleetAddress, { authorization })` for explicit
absent-signal setup, including Docked Fleets. The Profile pays current signal
rent and retains its storage reserve; transaction fees are separate. Existing
signals require refetch/reuse. Confirm setup and refetch before detection.

Use `planDetectSignal(ctx, fleetAddress, { patternId, focus, authorization })`
from `@aephia/atlas-kit/scanning/actions` for explicit detection. It requires a
directly owned Idle Fleet with current ship definitions, an existing Empty
signal, elapsed cooldown and the selected cargo costs aboard. Choose the pattern
and focus explicitly. Setup, cargo loading and movement must be confirmed and
refetched separately; no automatic lifecycle orchestration is exposed. Inspect
and simulate the Plan before execution. Restricted-key permission sufficiency
and final outcomes remain chain-authoritative.

Use `deriveScanSignalTiming(signal, { slot, unixSeconds })` for explicit-clock
cooldown/entropy/expiry observations, and `deriveScanRecoveryRange(signal,
location)` for exact raw geometry. These do not authorize actions or establish
acknowledgement eligibility; abandoned acknowledgement may shorten cooldown.

## Explicit scan completion

Use `planResolveSignal` on a pending signal within the supported reveal window;
then refetch after confirmation. A live Contact can be recovered with
`planRecoverSignal` once the Idle Fleet is in range, or explicitly discarded
with `planAbandonSignal`. Use `planExpireSignal` after contact expiry and
`planForfeitScanSignalEntropy` for an overdue unresolved attempt. Clear terminal
results with `planAcknowledgeScanResult`; keep the receipt first if needed.
All are from `@aephia/atlas-kit/scanning/actions` and take `(ctx, fleetAddress)`;
recover, abandon and acknowledge additionally take `{ authorization }` with
explicit direct Profile authority. The other three are permissionless gameplay
instructions but still need an explicit transaction fee payer. Each Plan stands
alone; never combine lifecycle steps or assume a random result in advance.
Every transition advances sequence: confirm, refresh, then plan again. No
implicit movement, polling, signing or retries. Nominal cargo/XP are not awarded
amounts; read the final receipt. Witness refresh and signal closure are not
supported planners.
