---
id: "receiving-packages"
title: "Receiving packages"
description: "The managed source transfer, the one supported way to bring a package into your repository and to update it."
url: "/receiving-packages"
locale: "en"
revision: "596fbb75425a4dc7"
---

# Receiving packages

A package arrives in your repository as **source**: its folder is copied unchanged into
`packages/<name>`, and a provenance record says exactly which bytes came from which source state.
There is no registry and no built artefact. Your installation, tests and builds never need a path
to the repository the package came from.

## What you need

- A **pnpm workspace** whose `packages` include `packages/*`.
- **Node.js 26** or later.
- A `.gitattributes` entry that keeps the transferred bytes exactly as they are, so a checkout on
  Windows does not convert line ends:

  ```text
  packages/** -text
  ```
- A **TypeScript-aware build**: packages ship TypeScript source (`exports` point to `src/`). Vite
  compiles a linked workspace package as it is; for Next.js, list the package in
  `transpilePackages`.

## The two sides

| Side | Tool | What it does |
| --- | --- | --- |
| the source repository | `rps-transfer` | composes a **bundle** for the packages you request: their folders, what they need, provenance |
| your repository | `rps-apply` | the **applier**: plans and applies a bundle in your repository, and checks your managed state |

The applier is two files, `rps-apply.mjs` and `rps-contract.mjs`, that you keep in your own
repository under `tools/rps/`. Every bundle carries a reference copy of them under `tooling/`, so
you can take them from the first bundle you receive. Review them before you commit them.

> [!NOTE]
> **Where the bundles come from today**
>
> Every package in the [catalogue](/catalog) is maintained in Foundation, which composes its
> bundles with `pnpm rps:transfer bundle --products <product> --owners <owner> --channel <channel>
>   --out <folder>`. A product (such as `active-ui`, `active-grid` or `active-i18n`) brings the owners
> it needs along; an owner (`active-format`, `active-virtual`, `active-helpers`, `eslint-config`) is
> requested on its own with `--owners`. The lint preset never comes along by itself: add `--owners
>   eslint-config` when you want it in your set. `typescript-config` is not transferred this way; see
> the [development basis](/development-basis). A bundle always holds your whole managed set: a
> package left out of it is removed by the update. Ask the maintainer for a bundle; you never need
> access to the source repository yourself.

## Receiving a package, step by step

From the root of your repository, in this order:

1. **Receive.** Plan the bundle, then apply exactly that plan:

   ```bash
   # The ordered decision. Changes nothing; prints the operations and a digest.
   node tools/rps/rps-apply.mjs plan --bundle <bundle-folder> --development

   # Apply exactly that plan.
   node tools/rps/rps-apply.mjs apply --bundle <bundle-folder> --expect-plan <digest> --development
   ```

   The package now sits in `packages/<name>`. Do not edit it there.
2. **Declare** the package where you use it, for example in your app's `package.json`:

   ```json
   {
     "dependencies": {
       "@workspace/active-format": "workspace:*"
     }
   }
   ```
3. **Install:**

   ```bash
   pnpm install
   ```
4. **Verify** that the app resolves the managed copy:

   ```bash
   node tools/rps/rps-apply.mjs verify-install --from <app-folder>
   ```

   The output must name every package you declared, each as `current`:

   ```text
     current      @workspace/active-format 1.0.0 from apps/web (workspace link: packages/active-format)
   ```

   `verify-install` checks only the managed packages the app declares. If it lists no package, it
   checked nothing: declare the package (step 2) and install (step 3) first.
5. **Use** the package; see its Getting started page, for example
   [active-format](/packages/active-format/getting-started).

## Channels

- **release**: carries only recorded releases, each with exactly its recorded content.
- **development**: a visibly marked trial. The applier refuses it unless you pass `--development`,
  and the provenance records it. A development transfer is never a release.

## Updating

An update brings your **whole managed set** to one new source state: every managed package moves
together, and the plan shows which files are kept, updated, added or deleted. You decide when you
update. A release in the source repository never changes your repository by itself.

**Request the whole set every time.** Your repository has one provenance record from one source. A
managed package that is missing from the next bundle is **deleted** by that update. Ask for a bundle
of every package in the `request` of your `.rps/provenance.json` (plus any you want to add), and
read the plan before you apply it: its line `Requested packages kept: …; added: …; removed: …` must
say `removed: -` unless you mean to remove a package.

Afterwards, repeat steps 3 and 4: install, and verify every app that uses the managed packages.

## Local changes and conflicts

**Never edit a managed file.** A change in a managed folder blocks the next update:

- `RPS-E4`: a managed file differs from its recorded hash;
- `RPS-E5`: a folder the bundle would add already exists and is not managed;
- `RPS-E16` and `RPS-E17`: a managed file is missing or replaced by something else.

The plan names each conflict and changes nothing. Make the improvement in the source repository
and receive it with the next transfer. An interrupted application is recovered with
`rps-apply recover --rollback`; `rps-apply status` shows your managed state, and
`rps-apply reconcile --bundle <folder>` compares your files with a bundle without changing anything.

## Your version and the newest version

Your `.rps/provenance.json` records the packages, versions, channel and source state you use. The
package pages of this site show the version Foundation documents. When the two differ, read the
package's changelog between your version and the documented one before you update.
