Appearance
Share Policies
Share policies control who can sync a document. During P2P sync, getShared() checks the share policy before sending documents to a peer.
Structure
Set the share field when creating a document:
typescript
await store.add('bookmark', {
uid,
url: 'https://example.com',
share: {
public: { license: 'SRL' },
}
});Policy types
Public
Anyone can sync this document:
typescript
share: {
public: { license: 'SRL' }
}User-scoped
Only specific users can sync:
typescript
share: {
users: {
[aliceUidHex]: { license: 'SRL' },
[bobUidHex]: { license: 'SRL' },
}
}Group-scoped
Members of a group can sync:
typescript
share: {
groups: {
[groupId]: { license: 'CC-BY' }
}
}Self (owner-only sync)
Sync only to the owner's own devices — personal data like read cursors, preferences, or draft content:
typescript
share: {
self: true
}This is the minimal share policy. The document syncs between the owner's phone, laptop, and tablet, but is never sent to other users.
Inherited
Inherit the share policy from the parent document. Use the literal string 'parent':
typescript
share: { ref: 'parent' }The store resolves 'parent' at sync time, walking up to the document referenced by parent and applying its share policy. This is the canonical pattern for child documents (e.g. comments under a discussion).
Children with no share are local-only
If you create a document with parent set but no share field, it stays on the author's machine and never syncs — even if the parent's $child write rules allow it. There is no warning at write time; the peer simply never sees it.
Always set share: { ref: 'parent' } (or a more specific policy) on children that should follow the parent's sharing.
License types
| License | Description |
|---|---|
'SRL' | Standard Reason License — attribution, no commercial use |
'CC-BY' | Creative Commons Attribution |
'PRIVATE' | Private, no redistribution |
Updating share policies
Share policies can be changed with edit():
typescript
await store.edit(uid, { hash }, {
$set: {
share: {
users: {
[newUserHex]: { license: 'SRL' }
}
}
}
});Whether a user can change the share policy depends on the document's write rules.
Custom share resolver
When the built-in policies aren't enough, provide a shareResolver callback on the DocumentStore constructor. It runs as a fallback — only called when none of the built-in policies (public, users, self, ref, membership tokens) granted access.
typescript
const store = new DocumentStore({
storage,
shareResolver: async (share, accessorUid, doc) => {
// share: the document's share policy object
// accessorUid: the peer requesting access (Buffer)
// doc: the full rendered document
// Example: identity docs are shared with contacts
if (doc._type === 'identity') {
return await isContact(doc.hash, accessorUid);
}
return false;
},
});When it's called
During sync, document-store evaluates access for each document in this order:
- Owner check (
doc.hash === accessorordoc.uid === accessor) share.publicshare.users[accessor]- Membership token matching
shareResolver()— if all above denied access
Return true to grant access, false to deny.
Use cases
- Contact-based sharing — share identity documents with all contacts without listing each one in
share.users - Type-based sharing — share all documents of a certain type with specific peers
- External data — check an external database or service to decide access
The callback receives the full rendered document, so it can inspect any field. It's async, so it can query databases or wish-core.
Performance
The resolver is called per document during sync access checks. Keep it fast — avoid expensive queries that don't cache well. For large-scale group access, prefer membership tokens instead.
No share policy
Documents without a share field are only stored locally. They won't be sent to peers during sync — not even to the owner's other devices. If you want private documents to sync across your own devices, use share: { self: true }.