Skip to content
Next — unreleased

Progression belongs to a Character, not a wallet-wide balance pooled across Profiles. Activities earn career XP; career levels provide career research points and contribute toward Council Rank Renown. Research turns those points into capabilities.

These are different units:

ValueMeaning
Career XPActivity experience recorded in that career’s earned field.
Career levelThe level stored on the Character, governed by the Game’s XP thresholds.
Career research pointsOne per career level; available points are level - spent, clamped to zero.
Council Rank RenownA separate research currency; available Renown is earned - spent, clamped to zero.

The six careers are Pilot, Data Runner, Mining, Building, Crafting, and Combat. The current PTR configuration awards one Renown per six career level gains across those careers. This counts levels gained, not unspent career points: spending a career point is not an exchange into Renown. For example, three Pilot and three Mining levels provide three research points in each career and one Renown before spending. Daily XP is a separate category, not one of these six careers.

Do not hardcode six for another deployment or future release. The Game exposes xpDefinitions.renown.divisor and feederCategories. The Character’s xp.councilRankLevelCarry records career-level progress toward the next Renown. Stored Council Rank level and available Renown are distinct values.

All Character-local inputs are already available from one Character account read, with no Game or research catalog required. Repeated reads use the context cache; use { refresh: true } when you need current state.

Requires an existing ctx and a known profileAddress (verified by the guide snippet gates):

import {
getCharacterForProfile,
deriveCharacterResearchState,
} from '@aephia/atlas-kit/identity';
const character = await getCharacterForProfile(ctx, profileAddress);
const research = deriveCharacterResearchState(character);
research.careers.pilot.earnedXp;
research.careers.pilot.level;
research.careers.pilot.researchPointsAvailable;
research.renown.available;
research.nodes; // acquired { nodeId, level } rows
JSON.stringify(research); // bigint values become decimal strings

Do not calculate career points as earned - spent: career earned is raw XP while career spent counts research points. Keep the arithmetic in bigint. The helper returns an immutable, JSON-serializable projection without any extra reads or cache. careers contains all six careers, with earnedXp, level, researchPointsSpent and researchPointsAvailable. renown separately exposes earned, spent, available, level and careerLevelCarry. Daily XP is not included in the career balances; it remains available on character.xp.dailyXp.

nodes includes acquired nodes only, not a historical receipt of purchases. An unlocked node without a stored nodeLevels entry defaults to level 1; a node absent from unlockedNodes is not acquired. Stored levels are reported as-is, including 0; only a missing entry defaults to 1. This preserves observed state rather than the official client’s minimum-1 normalization for synchronization estimates. The original stored rows remain on character.modifiers.nodeLevels.

Catalog costs use category labels that map to projection fields as follows:

xpCosts[].categoryBalance source
pilot, mining, building, crafting, combatcareers.<same>.researchPointsAvailable
data-runnercareers.dataRunner.researchPointsAvailable
council-rankrenown.available
dailyNot projected; character.xp.dailyXp.earned - spent, clamped to zero

This mapping identifies balances; it does not check all node prerequisites or authorize a purchase.

The projection reports xpSequenceId, researchSequenceId and pendingNodeUpdates from the input. It does not fetch definitions or assert that the Character is synchronized. Refresh through getCharacterForProfile and project again when the underlying read needs updating.

Add thresholds and node descriptions when needed

Section titled “Add thresholds and node descriptions when needed”

Requires the same context and Profile address (verified by the guide snippet gates):

import {
getCharacterProgressionForProfile,
getResearchCatalog,
} from '@aephia/atlas-kit/identity';
const progression = await getCharacterProgressionForProfile(
ctx,
profileAddress,
);
const renownRules = progression.xpDefinitions.renown;
const thresholds = progression.xpDefinitions.levelThresholds;
const catalog = await getResearchCatalog(ctx);

The progression read joins Character XP to sequence-matched Game XP rules; it does not invent levels. These optional definition reads load the large Game account through the shared lazy registry. Reuse the context so definitions and Character reads can be cached/deduplicated; do not fetch a Game per career or per node. Separate Character/catalog reads are not an atomic chain snapshot.

Stored levels reflect the rules at character.xp.seqId. If that sequence differs from the Game XP definitions, the progression read throws RegistryOutOfSyncError. The Character must be synchronized with the current game rules before relying on its levels; refreshing a read alone does not synchronize on-chain state.

Catalog nodes expose xpCosts with category, minimumLevel, and costRaw. Despite the field name, a career-category cost is career research points; council-rank costs are Renown. Daily XP is a separate currency. Minimum level is an eligibility threshold, not a deduction. Nodes also expose ATLAS and cargo costs, prerequisite requiredTagIds, maxLevel, cost growth and modifier. Node IDs and prerequisite tags must not be treated as interchangeable.

For context, these dated PTR definitions were read on October 11, 2026; they are not an eligibility or successful-purchase claim:

NodePointsOther requirements/costs
Fleet Size - Tier 1 (1)1 Renown0 ATLAS; prerequisite tag
X-Small Ships (31)1 Pilot research point350 ATLAS; prerequisite tag
Contested Space Scanning (18)1 Data Runner research pointData Runner level 5; 350 ATLAS; cargo; prerequisite tag
Enemy Faction Scanning (19)1 Data Runner research pointData Runner level 22; Combat level 5; 350 ATLAS; cargo; prerequisite tag

The Points column lists only nonzero point costs. Zero-cost category entries carry level requirements only (including Council Rank level 0 for X-Small Ships and Combat level 5 for Enemy Faction Scanning).

The catalog had 243 definitions, not necessarily 243 visible nodes or 243 total spendable points. The game client can hide or disable nodes. Read current costs instead of assuming a universal 350 ATLAS fee or a fixed total tree price.

Join acquired node IDs to catalog id values for names and descriptions; retain IDs that do not resolve rather than silently discarding acquired state. Applied perks live in character.modifiers.values. fleetSize limits ship capacity within a Fleet; fleetConcurrency limits Fleet count. Compare the latter with character.counts.fleets when displaying remaining Fleet slots. That comparison alone is not a complete action-eligibility check.

Treat these applied perks as current only when character.modifiers.seqId matches catalog.sequenceId and character.modifiers.unupdatedCount === 0. After a research-definition update, synchronization rebuilds the modifiers; until it finishes, values such as Fleet concurrency may lag current rules. Synchronize the Character in the game, then refresh it and the relevant definitions before presenting those limits as current.

Use planUnlockResearchNode to raise one node to its next level for your Profile’s Character. Name the node and the level it will reach: a node you have not unlocked reaches level 1, and a repeatable node (maxLevel 0) at level 3 reaches level 4. The planner never picks a level or unlocks prerequisites for you.

This example is illustrative. Supply your application’s context, the System of a Starbase where your Character has a Starbase Player, and explicit authorization; it only prepares an inspectable Plan.

import { planUnlockResearchNode } from '@aephia/atlas-kit/identity/actions';
import type { Address, SageContext } from '@aephia/atlas-kit/client';
declare const ctx: SageContext;
declare const profile: Address, authority: Address, system: Address;
const plan = await planUnlockResearchNode(ctx, 62, {
authorization: { profile, authority, keyIndex: 0 },
system,
level: 1,
});
console.log(plan.describe());

The unlock spends, all at once:

  • the listed research points from each career, or Renown for Council Rank;
  • the node’s ATLAS cost from the Character, grown by costGrowthBasisPoints for each level already held and rounded down (350 ATLAS at 10% growth costs 385 ATLAS from level 1 to 2);
  • any cargo cost from that Starbase Player’s cargo, grown and rounded the same way;
  • lamports from the Profile for the Character’s extra storage. The unlock pays them as a locked rent deposit, separate from transaction fees.

The Character grows when the unlock adds entries it did not hold: a newly unlocked node, a stored level, and any research tag, cargo category or rare mineral the node grants. A repeated level whose grants the Character already holds adds nothing. The program takes the growth’s rent from the Profile vault with the signing key, and returns any surplus storage lamports on the Character to the Profile. The Plan states the conservative Profile debit. A positive debit needs a Profile AUTH key or a key that also holds drainSolVault, and Profile lamports above the Profile’s own rent reserve.

Every required tag must already be on the Character, and each listed category must be at its minimum level, even when it costs nothing. Any Starbase where the Character has a current Starbase Player works, including another faction’s. Use a direct Profile AUTH key or a doResearch key scoped to SAGE or the Game.

When something is missing, planning fails with the exact shortfall, such as the missing tags or how many research points the Character has. Daily XP costs are refused as unqualified.

A Character whose research or XP predates the current Game definitions is also refused, because the resynchronized Character decides eligibility. The program resynchronizes it itself at the start of the unlock, as it does for any action that validates the Character, when at most 8 node levels are pending. Run such an action and replan. With more pending levels, run the permissionless syncCharacter instruction until no nodes remain.

Execution rechecks every requirement, both definition sequences, the ATLAS cost and the Profile storage debit before signing. The program adds the node’s tags and modifier to the Character, so read the Character after confirmed execution for the resulting limits. One unlock per Character fits in a transaction.

The bindings contain per-career XP allowance banks, regeneration rates and bank ceilings. These support a refillable allowance model; they do not establish mainnet progression times or guarantee a particular activity’s awarded XP.

Research time is not a field in the current node definition. Locker-based fee waivers, Starbased XP migration, respec/refund rules and mainnet progression targets are not established here as active contracts. Do not infer them from older announcements.

The point arithmetic above matches researchPointsEarned and spendableResearchPoints in the retained official-client evidence (excerpts and bundle SHA-256, inspected October 11, 2026). The read-only PTR observations were at slots 59,198,431–59,198,438, Game XP sequence 26 and research sequence 0. They establish stored configuration. Unsigned simulations on October 19, 2026 verified the unlock’s deductions, growth and failures (research unlock evidence); no research transaction was signed or submitted.