Share Data From Your Backend
Some apps share data without the user clicking a share button. A team chat app
can share each new room with the whole team as soon as it's created, or a
backend can add a member to a room when your own rules say so. In both cases your app calls
puter.kv.share() for the user. The user gives your app
permission once, and after that it shares without a prompt each time.
Ask for Permission Once
Ask with puter.perms.request(), naming the part of your
app's data the shares will cover and the highest access level they'll give:
await puter.perms.request('kvShare', { prefix: 'room:', access: 'write' });
The user sees a prompt asking whether your app may let other Puter users it
picks read and change the room: section of its data in their account. This
returns true once they allow it, and later calls return true without asking
again.
- Prefix. The permission covers the prefix and every key under it, so
room:coversroom:<id>:for every room. It has to name part of your app's data. A prefix that would cover all of it, such as'', is rejected withinvalid_argument. - Level. The permission covers the level you ask for and the levels below
it, so
'write'also covers'read'and'watch'shares. The default is'watch'. Asking for a higher level later shows the user a new prompt. - Your app's data only. An app can share only the data it keeps for the user, never another app's.
Sharing outside the prefix, above the level, or after the user withdrew the
permission is rejected with events_kv_handle_not_delegated. Shares made
before the user withdrew it stay in place.
Share From Your App
Once the user allows it, your app can share any room under room: with no
prompt. This creates a room and shares it with everyone on the team:
const team = ['alice', 'bob', 'carol'];
const prefix = `room:${crypto.randomUUID()}:`;
await puter.kv.set(`${prefix}meta`, { title: 'Launch planning' });
for (const username of team) {
await puter.kv.share({ prefix, recipient: username, access: 'write', label: 'Launch planning' });
}
Share From a Worker
A worker can share for the user too, which keeps the logic on
your server. In a worker, user.puter acts as the user who called it, from the
app that called it. So the share is of your app's data in the user's account,
and the permission from the first step applies:
router.post('/rooms/:roomId/members', async ({ request, params, user }) => {
const { username } = await request.json();
const share = await user.puter.kv.share({
prefix: `room:${params.roomId}:`,
recipient: username,
access: 'write',
label: 'Launch planning',
});
return { holder: share.holder, access: share.access };
});
Your app calls it with puter.workers.exec(), which sends the
user's token so the worker gets user.puter. In this call, workerUrl is your
worker's URL:
const response = await puter.workers.exec(`${workerUrl}/rooms/${roomId}/members`, {
method: 'POST',
body: JSON.stringify({ username: 'dave' }),
});
const added = await response.json();
In the same worker, me.puter is your own account as the worker's owner, so it
can't share the user's data. Use user.puter to share what the user keeps in
your app.
A user can still share from your app directly, since the permission belongs to your app and not to the worker. A check in the worker applies to the shares that go through the worker.
What the Holder Needs
Shares made this way work like any other. The holder finds them with
puter.kv.listShared() in your app, as shown in
Share a List With Someone.
Using a share needs no permission from the holder, because your app can only
use shares of its own data.