> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ctrlarcz.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Local development

> Install the monorepo, build the SDK, run the demo apps, and run unit, integration, and contract tests.

Ctrl+ArcZ is a **pnpm monorepo**. Everything below runs from the repo root unless
noted.

## Layout

<CardGroup cols={2}>
  <Card title="packages/sdk">
    `@ctrl-arcz/sdk`, the published TypeScript SDK: the risk firewall, protected
    transfers, the payer-side shield, and the wallet-side CCTP and Gateway bridge.
  </Card>

  <Card title="packages/contracts">
    The Foundry project: the protected-transfer contract, the spend-policy account and
    factory, the vault, and the stealth announcer.
  </Card>

  <Card title="packages/demo-kit">
    The shared session and design system, plus the server-only helpers the API calls
    (`cosignServer`, `relayServer`, `gaslessServer`, `investigator`).
  </Card>

  <Card title="apps/sender">
    The web app on port 5173. Everything the browser does, including the bridge, which
    is non-custodial and signs from the user's own wallet.
  </Card>

  <Card title="apps/api">
    `@ctrl-arcz/api` on port 8787: the co-signer, the gasless claim, the relayer
    routes, and the investigator. Every key in the system lives here.
  </Card>

  <Card title="apps/keeper">
    An autonomous agent that reclaims expired transfers and pays for its own gas out
    of a PULL box. See [The keeper](/guides/keeper).
  </Card>
</CardGroup>

<Note>
  There is no `apps/receiver` any more, and no Expo or React Native app in this
  repository. Receiving happens in the sender app's receive mode. The mobile client
  is a separate native Kotlin project, held to this repo by shared test vectors
  rather than by shared code.
</Note>

## Install

Requires Node 20+ and pnpm (the repo pins `pnpm@9.15.9`). The contracts submodule
also needs [Foundry](https://book.getfoundry.sh/).

```bash theme={null}
pnpm install
```

## Build the SDK

Most work needs the SDK built first, since the apps consume it as a workspace
package.

```bash theme={null}
pnpm --filter @ctrl-arcz/sdk build   # tsup -> dist
```

To rebuild on change while you work on the SDK:

```bash theme={null}
pnpm --filter @ctrl-arcz/sdk dev     # tsup --watch
```

You can also build every package at once with `pnpm build` (runs each package's
`build`).

## Run the apps

Two processes, two terminals. The web app is useful on its own; the API is what
the co-signer, the gasless claim and the relayer routes need.

```bash theme={null}
pnpm dev:api        # http://127.0.0.1:8787
pnpm dev:sender     # http://localhost:5173
```

The keeper is optional and runs on demand:

```bash theme={null}
pnpm --filter @ctrl-arcz/keeper once   # one tick, then exit
pnpm --filter @ctrl-arcz/keeper dev    # keep polling
```

### Environment

**Every signing key lives in `apps/api/.env`.** Copy `apps/api/.env.example`, which
annotates each one:

```bash theme={null}
PORT=8787
COSIGNER_PK=0x...        # The Machine: signs Private Pay spends after the firewall
RELAYER_PK=0x...         # pays gas for the gasless claim and the relayer routes
CIRCLE_CLIENT_KEY=       # Circle Modular Wallets, optional
CIRCLE_CLIENT_URL=
ANTHROPIC_API_KEY=       # enables POST /api/investigate, optional
CORS_ORIGINS=https://ctrlarcz.xyz,http://localhost:5173
```

<Warning>
  `CORS_ORIGINS` is fail-closed. An empty list allows **no** browser origin; it does
  not mean "any". Leaving it blank does not open the API up, it answers 403 to every
  web app, which is the safe direction to be wrong in. Running locally means adding
  `http://localhost:5173` to it, or the relayer-funded flows return 403 from a page
  that otherwise looks like it is working.
</Warning>

The web app's own `apps/sender/.env.local` holds no secret in production. In
development it takes dev-only, throwaway Arc-testnet values:

```bash theme={null}
VITE_DEMO_PK=0x...          # dev-only test-wallet signer (stands in for MetaMask)
VITE_DEMO_RECEIVER=0x...    # the poisoning demo crafts a lookalike of this
```

<Note>
  A `VITE_`-prefixed value is inlined into the client bundle whenever client code
  reads it through `import.meta.env`, so a signing key must never be one. A
  production build that would inline `VITE_DEMO_PK`, `VITE_RELAYER_PK` or
  `VITE_CLIENT_KEY` is refused by an `assertNoLeakedSecrets` build guard unless you
  explicitly set `VITE_ALLOW_DEMO_KEYS=1`, and then only with throwaway testnet keys.
</Note>

The bridge does **not** need a key. It is non-custodial and signs from the user's
own wallet in the browser, so nothing about it lives on the server. See
[Bridge USDC](/guides/bridge). The co-signer, the gasless claim and the relayer
routes do run server-side; see [The co-signer](/guides/co-signer) and
[The API](/reference/api).

## Unit tests

The SDK's unit suite runs under vitest and needs no chain.

```bash theme={null}
pnpm --filter @ctrl-arcz/sdk test    # vitest run
```

Run the whole monorepo's tests with `pnpm test`, or watch with
`pnpm --filter @ctrl-arcz/sdk test:watch`.

## Integration tests against a local anvil

The integration suite is the end-to-end proof that the SDK's digest and signatures
are accepted by the deployed contracts on a real EVM. It is excluded from the
default unit run and is gated behind `INTEGRATION=1`, so it is skipped unless anvil
is up and the flag is set.

<Steps>
  <Step title="Build the contracts">
    The tests deploy from Foundry's `out/` artifacts, so build them first.

    ```bash theme={null}
    pnpm contracts:build     # forge build
    ```
  </Step>

  <Step title="Start anvil">
    Run a local anvil node (Foundry). Keep it running in its own terminal.

    ```bash theme={null}
    anvil --host 0.0.0.0     # local node on http://127.0.0.1:8545
    ```
  </Step>

  <Step title="Run the integration suite">
    In another terminal, run the gated suite. The `test:integration` script sets the
    flag for you.

    ```bash theme={null}
    pnpm --filter @ctrl-arcz/sdk test:integration
    # equivalent to: INTEGRATION=1 vitest run test/integration
    ```

    The suite uses anvil's default accounts as owner, co-signer, merchant, and vault,
    deploys USDC and the factory, then drives `createEphemeral`, `readAccount`, and
    `settlePrivatePayment` against them.
  </Step>
</Steps>

## Contract tests

The Solidity tests run under Foundry, independent of the TypeScript side.

```bash theme={null}
pnpm contracts:test      # forge test -vv
# or, in packages/contracts:  forge test
```

## Quality checks

```bash theme={null}
pnpm typecheck           # tsc --noEmit across packages
pnpm lint                # eslint
pnpm format:check        # prettier --check
```

## Next

* Try the flows: [Quickstart](/getting-started/quickstart).
* Understand the design: [Overview](/concepts/overview).
* Handle failures: [Errors and result shapes](/guides/errors-and-results).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.