# Part 10 — partial sync

Partial sync is off by default. Turning it on tells the relay something it can
filter on, and **everything you tell it is readable forever**.

```ts
const engine = await ArgalaEngine.open({
  ...,
  metadata: {
    kind: true,                                   // send the record's kind
    shard: (r) => (r.data.channelTag as string),  // an app-chosen label
  },
});

await engine.openGraph('g:acme', { filter: { kinds: ['msg'] }, name: 'messages' });
engine.subscribe('g:acme', { filter: { kinds: ['msg'], shards: [tag] }, name: 'one-channel' });
```

Each subscription keeps its **own cursor**, because an unfiltered and a
filtered view of one graph are at different places in the same sequence.

**Subscriptions come from your app, not from the keys on disk.** A restart
does not rebuild them — the engine knows which graphs it holds keys for, but
only your `openGraph`/`subscribe` calls say what to pull. Call them on every
boot, as the quickstart does, or a restarted device will push its queue and
pull nothing. (Accepting an invite is the exception: it takes an optional
`SubscriptionSpec` and defaults to the whole graph, because accepting an
invite is an explicit request for one.)
`name` keeps them apart across restarts; it defaults to a stable hash of the
filter, which is right unless you change a filter's meaning without changing
its shape.

**What each field leaks:**

- `kind` says "this is a message, that is a task". Usually mild, occasionally
  not — a `kind: 'crisis-plan'` is a sentence.
- `shard` says as much as the label you choose. **Use a derived tag, not a
  channel name**: `await engine.personalId('shard', channelId)` gives you a
  stable opaque label. A shard must not change over a record's life — the relay
  filters on it, so a record that moved shards would vanish from a
  subscription that had already passed it.

Sending neither is the default and costs only the ability to sync a subset.

---
