# NightKit: guide for coding agents

You are adding NightKit to a Midnight dApp, or starting one. NightKit is 1AM's
connect kit for Midnight: one button that connects the 1AM wallet, or opens a
Midnight account from MetaMask, Coinbase Wallet, Phantom and other wallets.
Follow this guide exactly. Where it says stop, stop and tell the user.

## Status you must respect

- NightKit is a **preview**. It has not had a security audit. Do not present it
  to the user as production-ready, and do not wire it to a mainnet.
- It works on Midnight networks that run **ledger 9 or later** (`stagenet`, and
  the local devnet `undeployed`).
- If `npm view create-nightkit version` fails, the packages are not on npm yet:
  **stop and tell the user**. Do not substitute another package or write your
  own wallet code.

## Start a new app

```bash
npm create nightkit@latest my-app
cd my-app
npm install
npm run vault     # the NightKit vault on http://localhost:5174 — leave it running
npm run dev       # the app on http://localhost:5173
```

## Add it to an existing React app

```bash
npm install @1am/nightkit @1am/nightkit-react
npm install -D @1am/nightkit-vault
```

```tsx
import { NightKitProvider, ConnectButton, useNightKit } from "@1am/nightkit-react";

<NightKitProvider config={{ networkId: "stagenet", vaultUrl: import.meta.env.VITE_VAULT_URL, appName: "My app" }}>
  <ConnectButton />
  <App />
</NightKitProvider>;

function App() {
  const { session } = useNightKit();   // null until connected
  if (!session) return null;
  session.api;         // DApp Connector ConnectedAPI — the same for every wallet
  session.addresses;   // shielded, unshielded, coinPublicKey, encryptionPublicKey, userAddress
  session.vault;       // only when the account was opened from another wallet
}
```

Without React: `createNightKit({ networkId, vaultUrl })` from `@1am/nightkit`,
then `kit.wallets()`, `kit.connect("1am" | "evm" | "solana")`, `kit.subscribe()`.

## What you get

`session.api` is the standard Midnight DApp Connector API. Use it exactly as
you would 1AM's or Lace's:

- `getShieldedBalances()`, `getUnshieldedBalances()`, `getDustBalance()`
- `makeTransfer([{ kind, type, value, recipient }])` then `submitTransaction(tx)`
- `signData(data, { encoding: "text", keyType: "unshielded" })`
- `getProvingProvider(kmp)`, `balanceUnsealedTransaction(tx)`, `submitTransaction(tx)`
  for contract calls with midnight-js. Add `@midnightntwrk/ledger-v9` to the app
  for this.

Amounts are `bigint` in the token's smallest unit. Errors have
`type: "DAppConnectorAPIError"` and a `code`; `isUserRejection(e)` from
`@1am/nightkit` is true when the user said no — do not show that as an error.

## Rules. These are not style preferences.

1. **The vault must be on a different origin than the app.** In development
   that is another port (`:5174` beside `:5173`). In production it is its own
   domain (`vault.example.com`) that serves nothing else. Never serve the vault
   from the app's own origin, never proxy it under the app's path, never copy
   its files into the app's `public/` folder. NightKit refuses a same-origin
   vault; do not work around the refusal.
2. **Never handle keys in the app.** Do not import wallet-sdk, bip39 or key
   derivation code into the app. Do not ask the user's Ethereum wallet for a
   signature to "derive" anything yourself. Do not ask for, display, log or
   store a recovery phrase. The vault does all of this; the app only makes
   requests.
3. **Do not style, move or cover the `#nightkit-vault` iframe.** No opacity,
   transform, filter, animation or overlay on it or around it. The vault checks
   that it is plainly visible before it lets the user approve, and will refuse
   if it is not.
4. **Do not build your own approval dialog** for transfers or signatures. The
   wallet (1AM's popup, or the vault's sheet) shows the user what they approve.
5. **Do not offer wallets NightKit refuses.** Smart-contract, passkey and MPC
   wallets cannot open an account. With RainbowKit, set
   `coinbaseWallet.preference = "eoaOnly"`.
6. **Do not change the network endpoints from the app.** The vault decides
   where a network lives; the app picks it by name.

## Things users will ask you

- *"It says syncing for a long time."* A new account's first sync on a public
  testnet takes around twenty minutes. Addresses work at once; balances and
  sending wait for it. Later visits restore in seconds. Show progress with
  `session.vault.onSync`; do not block the page.
- *"Sending fails."* Sending and contract calls need a Midnight proof server.
  The default stagenet setup expects one on `http://localhost:6301`.
- *"Fees?"* A NightKit account pays fees from its own NIGHT unless the vault is
  configured with a fee sponsor. `session.vault.registerNightForDust()` sets up
  fee generation once the account holds NIGHT.
- *"Private app data that survives a new browser?"*
  `await session.vault?.appSecret("label")` returns 32 bytes only this account
  and this site can derive. It is absent for 1AM: keep a fallback.

## Before you say you are done

- The app runs, and Connect opens the wallet list.
- The app's origin and the vault's origin differ. Say both to the user.
- You added no key handling to the app.
- You told the user this is a preview on a testnet.
