# Crafting
Documentation: next (source: develop).

Install the preview: `pnpm add @aephia/atlas-kit@next`. These docs follow develop and may include changes not yet published to npm.

> Recipes, habs, and processes — turning inputs into outputs over time.

Markdown source of https://develop.atlas-kit-docs.pages.dev/guides/crafting/ — see https://develop.atlas-kit-docs.pages.dev/ai/ for the full machine-readable surface.

Crafting has three moving parts, and separating them makes the API obvious:

- a **recipe** is the rule: these inputs become that output, taking this long;
- a **hab** is the place: a plot at a starbase where crafting can happen;
- a **process** is the run: one recipe executing in one hab, right now.

This guide covers recipes and processes. The hab as a _facility_ — its
buildings, modifiers, capacity, and rent — has
[its own guide](/guides/crafting-habs/).

:::game[Recipes run inside Crafting Habs]
A recipe is the plan for making something: it lists the materials needed, what
the job produces, and how long it normally takes. A Crafting Hab is the
workshop where that recipe runs. Its size and quality affect how much work it
can handle and how efficiently it works. While a job is running, its materials
and workshop space are tied up until the craft finishes or is cancelled.
:::

## Recipes

Recipes are static definitions from the Game account, so they are the same for
everyone and cheap to read:

```ts
const recipe = await sage.recipes.byId(recipeId);
```

> **Runnable example — Read a recipe and crafting activity.** One recipe by id, plus the habs and processes a character runs. Run it in the browser at https://develop.atlas-kit-docs.pages.dev/guides/crafting/.

## Habs and processes

Both hang off whoever owns them, which is usually a character or their state at
a starbase:

```ts
const habs = await sage.craftingHabs.byCharacter(characterAddress);
const running = await sage.craftingProcesses.byCharacter(characterAddress);
```

A process is a lifecycle, not a boolean. It has a start, a duration derived
from its recipe, and an end — which is why crafting occupies a hab for a
period rather than completing instantly.

## Why processes need discovery

Crafting processes have **no derivable address**. You cannot compute where one
lives from the character or the recipe; it is assigned when the process is
created.

That is why the reads above are `byCharacter`, `byProfile`, and
`byStarbasePlayer` rather than a single `get(address)` — each encodes a
verified way of _finding_ processes, rather than pretending you can calculate
where they are. See [the bindings escape hatch](/concepts/bindings/) for the
broader pattern of accounts without derivable addresses.

## Running a process

The lifecycle above is also what you write against: starting a process,
collecting a finished one, and cancelling one that should stop are each one
planned transaction. If this would be your first write, read
[How Atlas Kit changes the game](/start-here/how-it-acts/) first.

**Starting** commits everything up front: the recipe, how many runs, the
inputs from your starbase storage, and the crew who will work it. Because a
process has no derivable address, starting one also names the fresh address
the new process will live at — you generate it, and its key signs once at
creation alongside your wallet:

```ts
import { generateKeyPairSigner } from '@solana/kit';
import type { PlanAuthorization } from '@aephia/atlas-kit/crafting/actions';
import { planStartCraftingProcess } from '@aephia/atlas-kit/crafting/actions';
import { executePlan } from '@aephia/atlas-kit/planning';

const authorization = {
  profile: character.profile.address,
  authority: authoritySigner.address,
  keyIndex: 0,
} satisfies PlanAuthorization;

const processKey = await generateKeyPairSigner();
const recipe = await sage.recipes.byId(recipeId);
const plan = await planStartCraftingProcess(ctx, recipe, base, {
  authorization,
  craftingProcess: processKey.address,
  quantity: 1n,
  numCrew: 2,
});

const result = await executePlan(ctx, plan, {
  feePayer: authoritySigner,
  signers: [processKey],
});
```

Keep `processKey` until the Plan has executed: the fresh key signs the
creation alongside your wallet, so it goes into `signers` — execution fails
without it. Afterwards the key has no further role; the process is found
again by discovery, never by re-deriving that address.

**Completing** collects a process that has reached the end of its duration.
Derive the lifecycle state first — the planner refuses a process that is not
actually complete at the timestamp you give it:

```ts
import { deriveCraftingProcessState } from '@aephia/atlas-kit/crafting';
import type { PlanAuthorization } from '@aephia/atlas-kit/crafting/actions';
import { planCompleteCraftingProcess } from '@aephia/atlas-kit/crafting/actions';

const authorization = {
  profile: character.profile.address,
  authority: authoritySigner.address,
  keyIndex: 0,
} satisfies PlanAuthorization;

const processes = await sage.craftingProcesses.byCharacter(characterAddress);
for (const process of processes) {
  if (deriveCraftingProcessState(process, nowUnixSeconds).kind !== 'complete')
    continue;
  const plan = await planCompleteCraftingProcess(ctx, process, {
    authorization,
    atUnixSeconds: nowUnixSeconds,
  });
  console.log(plan.summary);
  break;
}
```

**Cancelling** is the same shape as completing, for a process that has _not_
finished: `planCancelCraftingProcess` stops the run, and what comes back —
inputs, crew — follows the game's rules, decided at execution rather than
promised by the planner.

The timestamp you pass is used only for the local lifecycle check; the game
remains the authority on timing, capacity, and returns when the Plan
executes. Signing and executing works exactly as in [Warp &
subwarp](/guides/moving-a-fleet/#sign-and-execute-once).

## Gotchas

**A finished process still exists.** Completion is a state, not a deletion. Read
the lifecycle rather than assuming presence means "in progress".

**Completing is not automatic.** A process that reaches its duration sits
complete until someone collects it — [running a process](#running-a-process)
covers planning that collection.

**Hab capacity is finite.** A hab holds a limited number of concurrent
processes, so "can this character craft" is not answerable from the recipe
alone — [crafting habs](/guides/crafting-habs/) covers reading the free
slots.

**Recipe inputs are raw amounts.** Like all quantities, they are `bigint` and
need the cargo definitions to render as names and decimals.

## Reference

- [`crafting`](/reference/crafting/) — every export in this entry
- [`crafting/actions`](/reference/crafting/actions/) — the lifecycle planners
- [Crafting habs](/guides/crafting-habs/) — the facility itself
- [`starbases`](/guides/starbases/) — where habs live
- [`cargo`](/guides/cargo/) — the inputs and outputs
