# Subscriptions
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.

> Live account updates, reconnection, and cancellation.

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

Polling works, and for most applications it is enough. When it is not, the
context can subscribe to account updates instead.

Subscriptions are **context-owned**, which is the important detail: an update
arriving over a subscription goes through the same validation and lands in the
same cache as a normal read. A subscribed fleet and a fetched fleet are the
same cached snapshot, not two views that can disagree.

## Configure the reference provider

Inject an application-owned Kit `RpcSubscriptions<AccountNotificationsApi>` into
`createRpcSubscriptionProvider` from `@aephia/atlas-kit/client`. The factory does
not create a socket, accept a URL, or retain credentials. Nothing subscribes
until you explicitly call `ctx.data.subscribe()` or a capability's watcher.

Illustrative setup with an existing Kit connection and account provider:

```ts
import type { AccountNotificationsApi, RpcSubscriptions } from '@solana/kit';
import {
  createRpcSubscriptionProvider,
  createSageContext,
} from '@aephia/atlas-kit/client';
import type { AccountProvider } from '@aephia/atlas-kit/client';

function createLiveContext(
  rpcSubscriptions: RpcSubscriptions<AccountNotificationsApi>,
  provider: AccountProvider,
) {
  return createSageContext({
    cluster: 'zink-ptr',
    provider,
    subscriptions: createRpcSubscriptionProvider({
      rpcSubscriptions,
      identity: { kind: 'websocket', name: 'application-live' },
      backoff: { initialMs: 250, maxMs: 30_000 },
    }),
  });
}

// Pass your existing connections to createLiveContext(), then register through
// ctx.data.subscribe() or watchFleet(). Call ctx.dispose() on teardown.
```

The identity is optional and defaults to `websocket` / `rpc-subscriptions`.
Use only a non-secret label: identities are public provenance, never a place for
endpoints or authentication material. Backoff defaults to 250–30,000 ms; supplied
values must be positive safe integers with `maxMs >= initialMs` or the factory
throws synchronously.

The adapter requests base64 data at the registration commitment (`confirmed` by
default). Initial registration failure rejects with a sanitized `ProviderError`
and no automatic retry. Once registered, stream termination or failure starts
one retry loop per registration. Each wait uses equal jitter between half and
all of the capped exponential delay, rounded down to at least 1 ms. Successful
re-registration resets the delay and emits `reconnected` before account events.
Failed attempts never emit it. Transport exceptions and abort reasons are not
copied into errors or diagnostics. Listener exceptions do not trigger retries.

Explicit unsubscribe, caller abort, and context disposal cancel pending waits
and release each Kit registration via its abort signal. Iterator cleanup is
best-effort, so a transport that ignores cancellation cannot block teardown or
deliver later updates. No transport can guarantee updates during an outage;
the context closes that gap with its existing direct-read resynchronization.

Bring your own `AccountSubscriptionProvider` if your transport needs a different
integration. It must honor cancellation and emit `reconnected` only after a
successful registration; keep validation, caching, and resynchronization in the
context rather than duplicating them in the provider.

## Why the shared cache matters

Without shared identity, a live update and a fetch race each other, and which
one your application sees depends on timing. Here they cannot diverge: both
write to the same cache slot under the same key.

Updates are translated the same way reads are, so a subscription delivers the
SDK's own snapshot types rather than raw account bytes.

## Reconnection

Connections drop. When one does, the SDK resynchronises rather than resuming
blindly, because the gap between disconnect and reconnect is exactly where
missed updates hide.

Reconnects coalesce: a burst of drops produces one resynchronisation rather
than one per drop, and a reconnect that arrives while another is in flight is
folded into it rather than queued behind it.

## Cancellation

Subscriptions are cancellable, and disposing a context cancels everything it
owns. That matters in a browser, where a component unmounting must not leave a
socket delivering updates into a cache nobody reads.

## When to reach for this

Prefer polling with a sensible `maxAge` unless you have a reason not to. It is
simpler, it degrades more gracefully, and for state that changes on the scale
of minutes it is indistinguishable to a user.

Subscribe when you are tracking something that genuinely moves — an active
fleet, an order book — and staleness is visible.

## Reference

- [`client`](/reference/client/) — subscription exports
- [Caching and provenance](/concepts/caching/) — the cache updates land in
