Skip to content
DocsSharing a package

Packages

Share a package and manage access

Agent playbook — written for the agent connected to your account; you can read along

Access in Kody belongs to an organization. Resources (packages, apps, jobs, secrets, integrations, memory, email) live in one org, and a grant gives one person or team a preset on one resource. To let someone use a package without sharing a login, grant them Use on it.

Share source with one person

To let one person (inside or outside your organization) read a private package and fork it into their own Kody, without running yours or opening your app:

  1. Open the package's Collaborators tab and give them Read: pick the member, or in a personal org invite their email or username with Read. Agents call packageCollaboratorSet with "preset": "read" (a username outside the org becomes an outside collaborator), inviteCreate with kind: "grant" and "preset": "read", or inviteLinkCreate with "preset": "read". The CLI takes --preset read.
  2. They open the package page and click Fork, or their agent calls packageFork with { "package": "@owner/package-name" }.
  3. The fork is a new package in their own org. It records where it came from, so they see when your source moves ahead, and it runs and bills in their org.

Read is package:read only. They can view and git clone the source, but cannot run your package (no package:execute) or open your app (no app:execute). Forking never gives them more access to your package. Everyone with a preset that includes read (Read, Use, Contribute, Manage) and you as the owner can fork the same way. Someone without access still gets a 404 and cannot fork. Making a package public is a different choice: anyone on the internet can read and fork the default branch.

Presets

PresetOn a package
ReadRead, clone, and fork the source (package:read)
UseRead the source and run it (package:read, package:execute)
ContributeUse, plus write (package:write)
ManageContribute, plus publish, delete, and manage access for the package

Advanced grants take an explicit permission list instead of a preset. No preset includes billing:* or revealing a secret. On the web, Advanced: choose permissions under the preset picker sends the same list. Read applies to packages only; other resources start at Use.

Grant access

Call accessGrant from a request bound to the org that owns the package. You need manage access on the package.

{
	"resource_type": "package",
	"resource_id": "<package id>",
	"subject_type": "user",
	"username": "jesse",
	"preset": "use"
}

Everyone in this organization

To give every member of a team org the same access to one package, grant it to the org itself with subject_type: "org" instead of granting each person:

{
	"resource_type": "package",
	"resource_id": "<package id>",
	"subject_type": "org",
	"preset": "use"
}

The grant is checked live against org membership every time access is checked, so it covers current members and anyone who joins later, and someone who leaves the org loses it at once. Kody writes one grant, never one row per member. It covers only the package it is granted on; everything else stays private, and outside collaborators and other orgs get nothing. subject_id may be omitted or must be this org's id: an everyone grant cannot name another org, cannot target resource_type: "org" or an execution context, and is refused in a personal org (which has no other members).

An everyone team with Use on the package, plus an org invite link that adds new people to that team, keeps working the same way.

accessList shows who holds what across the org. accessRevoke removes one grant by grant_id. To change someone's preset, call accessGrant again for the same subject; it updates the grant.

Package collaborators

For one package, three calls cover teams, members, and everyone in the owning org. Each needs Manage on the package, and package_id accepts the saved-package UUID or the package name leaf (@owner/leaf or leaf).

  • packageCollaboratorList ({ "package_id": "…" }) lists everyone who holds a grant on the package: everyone in the org (kind: "org"), teams, members, and outside collaborators, each with grant_id, kind, username or team slug, and preset.

  • packageCollaboratorSet adds a team, an org member, or everyone in the org, or changes the preset of one already listed. Pass exactly one of team_slug, team_id, username, user_id, or everyone: true, plus a preset or an advanced permissions list. A person outside the org becomes an outside collaborator. Owners already have full access:

    {
    	"package_id": "release-notes",
    	"team_slug": "writers",
    	"preset": "contribute"
    }
  • packageCollaboratorRemove ({ "package_id": "…", "grant_id": "…" }) removes one collaborator, including an outside collaborator.

These run through accessGrant and accessRevoke, so they are checked and audited the same way.

On the web

People with Manage on a package see a Collaborators tab on the package page (/@owner/<package>/collaborators). It lists who has access and their preset, adds everyone in this organization, a team, or an org member, changes a preset, and removes access, using the package collaborator calls. Advanced: choose permissions under each preset picker sends an explicit permission list instead. Outside collaborators are listed and can be removed; their access is changed through accessGrant or packageCollaboratorSet. Org owners always have full access and are not listed.

In a personal org there are no members or teams to add, so the tab invites people instead:

  • Invite a collaborator takes an email or username and a preset: Read to read and fork the source without running it, Use to run it and read the source, Contribute to also update it, or Manage to also publish, delete, and choose who else has access. It calls inviteCreate with kind: "grant".
  • The invite message is shown once, with a copy button. Send it to the person so their agent can call inviteAccept. Open Details to see the message.
  • Pending invites lists invites that have not been accepted. Revoke calls inviteRevoke.
  • Once the person accepts, they appear under Who has access as an outside collaborator. Remove calls packageCollaboratorRemove.

The tab also links to creating a team org for group sharing through members and teams. Team orgs use member and team controls on this tab.

Access for one person

In a team org, an Owner opens Members, Collaborators, or a person-linked row on Grants, then Access. That page is /@<slug>/-/grants/user/<userId> (Access for {name}). It lists what that person can use in this organization and Give access grants or revokes a preset on specific packages, apps, and credentials in one submit. Team and everyone grants appear as read-only. Owners already hold everything, so there is nothing to grant them. A person with no membership or grant 404s.

The package Collaborators tab stays the place to share one package. Use Access when the work is one person across packages, apps, and credentials. Agents still call accessGrant, accessRevoke, and packageCollaboratorSet.

Open API

The same calls are on the Open API (/openapi.json):

CallCapability
GET /v1/packages/{package_id}/collaboratorspackageCollaboratorList
POST /v1/packages/{package_id}/collaboratorspackageCollaboratorSet
DELETE /v1/packages/{package_id}/collaborators/{grant_id}packageCollaboratorRemove
POST /v1/access/grantsaccessGrant
POST /v1/access/grants/{grant_id}/revokeaccessRevoke
POST /v1/access/invitesinviteCreate
POST /v1/access/invites/{invite_id}/revokeinviteRevoke

Invite someone who has no access yet

inviteCreate with kind: "grant" sends the same grant to an email address or username. The token is shown once, with a prompt for the invitee's agent:

{
	"kind": "grant",
	"email": "jesse@example.com",
	"resource_type": "package",
	"resource_id": "<package id>",
	"preset": "use"
}

The invitee calls inviteAccept with the token, or chooses Accept on the invite card on their Waiting page (an email invite needs a verified email there). Nothing attaches before they accept. Invites expire after 7 days; inviteRevoke cancels a pending one.

Use kind: "membership" to add someone to the whole team org instead (with a role and optional teams). Members hold the org-level basics plus what they are granted. Owners hold everything. Membership invites are refused on a personal (signup) org; create a team org to invite people.

Personal orgs can still grant Use/Contribute/Manage on packages via accessGrant / inviteCreate kind: "grant" and revoke with accessRevoke. The web Grants page and per-person Access page are aimed at team orgs. In a personal org, invite and remove collaborators from the package's Collaborators tab, or use those capabilities.

Unmappable credentials

A credential is unmappable when nobody who runs or visits the package can swap in their own, so it always acts as the owning org. A package has one while its published version declares a need with supplier: "publisher" (while the package-credential-suppliers flag is on) or ownerCredentialForVisitors: true (see Credentials). Anyone who can change or publish such a package could make its code use those credentials however they like, so Kody surfaces and guards it:

  • Share-time warning. Granting a preset that can change or publish the package (Contribute or Manage, or a custom list with package:write or package:publish) still succeeds, and returns a warning that names each credential (by name and need key, never values or ids) and says the grantee could make the package's code use them. It shows in the Collaborators tab on the web, after kody package grant, and in the warnings field of packageCollaboratorSet, accessGrant, inviteCreate, and inviteLinkCreate. The Collaborators tab also shows it on each existing grant it affects.
  • Owner-only changes. While the package has unmappable credentials, only org Owners (the owner of a personal org) can push, publish, or otherwise change its code or manifest. Existing collaborators are locked too, with no grandfathering, and the lock applies as soon as a credential becomes unmappable. Read and Use are unaffected: collaborators keep running it. Denials have code unmappable_credentials, name the credentials, and say how an Owner can allow the collaborator.
  • Allow one collaborator anyway. An org Owner can let one person or team change it despite the risk, per grant: the Allow changes despite unmappable credentials checkbox on their row in the Collaborators tab, kody package grant <package> --user <name> --preset contribute --allow-changes-despite-unmappable-credentials (--disallow-changes-despite-unmappable-credentials turns it off), or allow_changes_despite_unmappable_credentials: true on packageCollaboratorSet or accessGrant. Omitting it keeps the grant's current choice. It is not available on everyone-in-org or profile grants.
  • Owner notice. Owners see a notice explaining the lock on the package page, the Collaborators tab (unmappable_credentials on packageCollaboratorList), kody dev check / packageCheck (notices), and publish output (unmappable_credentials_notice). No email is sent.
  • Profiles. The check uses the acting principal through the profile chain. A profile acting for an Owner changes the package only when every layer of its chain holds the permission; a profile acting for a collaborator follows that collaborator's grant, including its allow option.

What a collaborator can do

Someone who holds a grant but is not a member is an outside collaborator. They must connect with your org selected at consent. Then they can:

  • Read and run the packages they hold Use on
  • Run ad hoc code that imports those packages
  • Search the shared catalog

They cannot see anything else in your org, reveal secret values, or change a package unless the grant says so.

Who pays when a Use collaborator runs your package

The org is the billing boundary, and a connection is billed to the org it is bound to. Jesse connects with the owning org selected, so everything his runs use is charged there:

MeterCharged to
Usage events, credits, org and per-user budgets, outbound fetchOwning org (the bound org)
Daily and weekly execute limits (unique execute workers per day)Owning org's execute pool

Execute limits are one org-wide pool that grows with the org's paid seats. A grant-only collaborator draws from the owning org's pool but is not a seat, so granting someone Use does not change your seat count or grow your pool. Nothing from these runs is charged to the collaborator's personal plan.

Example: Jesse has Use on @acme/bot. Connected as @acme, a run debits acme's credits and budgets and counts against acme's execute pool. Jesse's own personal-org limits are untouched by those runs.

Package apps are the exception. When Jesse opens an internal app of @acme in the browser (he holds a grant on it but is not a member), his own personal org pays for the metered work his visits cause, and acme keeps paying the app's Worker compute. Internet-visible apps stay paid by acme for every visitor. See Package apps: who pays for visits. See Organizations for how the pool is sized.

Secrets and integrations on a granted run

Every run acts as a person, and a credential resolves only when that person holds Use on it. When Jesse runs your package in your org:

  • Secret values and integration tokens come from your org
  • Jesse never gets secret:read from a Use grant
  • Each secret or integration the package reads needs Jesse's own secret:use or integration:use on it. A Use grant on the package does not include them; grant them on the credential: accessGrant with resource_type: "credential" and the credential_id from secretList (or integrationList, mcpServerList, secretProviderList). The grant follows the credential if it is renamed, and ends if it is deleted
  • The package rules then narrow that use: package:execute on the package, and each secret or integration must allow that package (host allowlist, allowed_packages, or self-authored rules for user secrets)
  • The same applies to {{secret:…}} in Jesse's own ad hoc code

See Secrets for placeholders and allowlists.

Triggers in a shared package

A package's jobs, subscriptions, and webhooks run only for people who turn them on for themselves with enablementEnable, and each run acts as that person with their live access. If Jesse turns on the nightly job in @acme/bot, it runs as Jesse, with Jesse's grants, and pauses if he loses access. Usage still counts against @acme. See Organizations.

The package's Collaborators tab also has Share with a link, in personal and team orgs alike, for when you do not know the person's email or username. Pick a preset, how many people may use the link (1 by default; blank for anyone who has it), and how many days it lasts (7 by default, up to 365). The link is copied once; Kody keeps only its hash.

Whoever opens the link sees what it gives: the package, the preset, and who shared it. They can create an account first if they do not have one. Opening the link changes nothing; they choose Accept or Decline. A signed-in person who opened it also finds it on their Waiting page and in the Invites panel on their Organizations page (/account/organizations) until they accept, decline, or the link stops working.

Accepting grants the preset on that one package. Someone who is not a member of the owning org becomes an outside collaborator; the link never adds anyone to the org. Accepting never lowers access: a person who already has Manage keeps it when they accept a Use link, and an Owner already has everything.

Each person can use a package link once. If you later remove them, the same link does not let them back in; send a new one. The tab lists active links with their uses and expiry, and Revoke stops one immediately. Links stop working when they expire, run out of uses, the package is deleted, or the person who made the link loses access. Creating, revoking, and accepting a link are in the org audit log (invite_link.created, invite_link.revoked, invite_link.joined).

Cross-org imports

A package imports only packages from its own org. A package in org A cannot import a package from org B, even with a grant. To reuse another org's public package, fork it into your own org and import the copy.

Which call to use

  • Who has access to one package: packageCollaboratorList
  • Add a team or org member to one package, or change their preset: packageCollaboratorSet; remove one with packageCollaboratorRemove
  • One person or team, already known: accessGrant
  • Someone without access or an account yet: inviteCreate with kind: "grant", or a link from Share with a link on the Collaborators tab when you do not know their email or username
  • The whole org: inviteCreate with kind: "membership" or orgMemberUpdate
  • Take access away: accessRevoke (a grant) or orgMemberRemove (a member)

Search these names first and open capability detail for the exact call shape.

Working with an agent? This page is also plain markdown at /docs/package-sharing.md, or load it over MCP with search({ entity: 'guide:package_sharing' }).