Private Pub

Docs

Teams

Every package belongs to a team. The people and CI systems on it can install it, and everyone else gets a 404.

For agents and LLMs: this page as markdown or llms.txt

How teams work

Every package belongs to one team. Its members can see it and install it, and to everyone else it doesn't exist.

Your account starts with a personal team called you@example.com's Personal Team, and a new package you publish starts there unless an optional private_pub.team names another team. To share packages, create a team, invite the people who need them and move the packages to it.

Each member signs in with their own account and adds their own token to pub. The server gives that token the role they have on each of their teams, so nobody has to share a token, and removing someone cuts off their access right away. CI uses a service account, or a team key for a single team, instead of a person's token.

Once you sign in, manage your teams at /teams.

Each team has its own plan. A free team can have up to 10 packages and publish archives up to 100 MB. An owner can upgrade a team to Premium on its page for $10 a month or $100 a year, for unlimited packages and archives up to 500 MB. Pricing compares the plans.

Roles

Each member has one of three roles on a team. The same person can own one team and be a reader on another.

Role What it can do
Owner Everything a writer can do, plus invite, remove and change members, open the team to an email domain, rename or delete the team, create and revoke team keys, add service accounts and move the team's packages to another team.
Writer Everything a reader can do, plus publish new versions and change package options, like discontinuing a package or retracting a version.
Reader See the team's packages and their activity on the site, and install them with dart pub get .

Every member can see who else is on the team. Only owners see its pending invites, the email domains that invite new accounts, and its legacy team keys. Everyone on the team can see its service accounts. Only an owner adds one or changes its role.

Invites are for readers and writers. To add an owner, invite them as a writer and make them an owner once they join.

Start a team

Anyone with an account can create a team, and they become its first owner.

  1. 1

    Create it

    Open Teams, enter a name under Create a team and click Create team. A name can be up to 64 characters, and owners can rename the team later.

  2. 2

    Invite people

    On the team page, enter the email each person signs in with and pick reader or writer. They get an email with a link to /teams, where they accept it.

  3. 3

    Move packages to it

    Open a package's Team tab, pick the new team and click Move. To skip this for new packages, publish them with a writer team key, and they start in its team, or set an optional private_pub.team. The package requirements explain that field.

Invites

Inviting someone by email sends them an email with a link to accept. The invite also waits on the site until they sign in and answer it. Opening the team to a domain does not send an email.

Invite the email the person signs in with. They don't need an account yet. Once they sign in, the invite shows at the top of /teams with Accept and Decline buttons.

Accepting only works once their sign-in provider has verified the email. If it hasn't, they verify it with the provider, then sign out and back in to accept.

Inviting the same email again changes the role of the pending invite. Owners can revoke an invite from the team page until it's accepted.

Owners can also open the team to an email domain, like verygood.ventures. Anyone with an email on that domain gets an invite to join as the reader or writer the owner picked, including accounts that already exist. They accept or decline it like any other invite. The domain has to match exactly, so example.com does not include sub.example.com. Someone who is already on the team, already has an invite, or declined one is not invited again. Allowing the same domain again changes the role for invites created after that. If several teams allow the domain, the account is invited to each of them.

Members

Owners change roles and remove members on the team page. Any member can leave a team from there too.

  • A team always keeps at least one owner. To step down as the last owner, make someone else an owner first.
  • You always own your personal team. You can't leave it, and nobody can remove you or change your role.
  • Someone who leaves or is removed loses access to the team's packages right away. The packages stay with the team, including the ones they published.

You can invite people to your personal team, but a team of its own is easier to hand over later, since a personal team always belongs to one person.

Moving packages

A package moves from its Team tab or from its team's page. Moving it hands it over to the other team completely.

You have to be an owner of the team the package is on, and a writer or owner of the team it goes to.

Everyone on the new team gets access to it. Anyone who was only on the old team loses it, and so do the old team's keys.

The package keeps its name and its versions, so projects that depend on it don't have to change.

The team it goes to needs room for it. A free team with 10 packages can't take another until one leaves or an owner upgrades it to Premium.

Service accounts

A service account is a machine, such as GitHub Actions or Codemagic, with one or more tokens of its own.

Any signed-in user can create one at /service-accounts, and they own it. Only that owner can rename or delete it, or create and revoke its tokens.

An owner of a team adds it as a reader or a writer, never as an owner. One token then reaches every team the account belongs to. From the service account page, the owner can add it only to teams they own. From the team page, an owner can add a service account they own, or one owned by someone already on the team. The account's owner can remove it from a team even when they are not an owner of that team. Deleting the account removes it from every team and revokes its tokens.

A service account cannot invite people, change billing or delete a team. Writers publish packages. They do not grant a machine access to a team. That takes an owner, the same bar as invites and team keys.

Role What it can do
Reader Install the packages of every team it's on.
Writer Install them, publish new versions and change package options. A new package starts in the one team it can write to. When it can write to several, set private_pub.team .

A new token starts with sa_. Requests are checked against its SHA-256, and a sealed copy is stored so the owner can show it again. It is also shown when it is created. Save it as a secret called PRIVATE_PUB_TOKEN and add it to pub before pub get.

Terminal
dart pub token add https://private-pub.dev --env-var PRIVATE_PUB_TOKEN

Revoking a token stops that token right away. The other tokens on the same account keep working. A token expires after 7, 30, 60 or 90 days, after a year, or never. The page shows when it was last used, which updates at most every 5 minutes. An expired token gets a 401 that says where to create a new one.

A new package published by a service account that can write to exactly one team starts in that team. When it can write to several, set an optional private_pub.team to the id of the team the package belongs in. A reader account can install but not publish. Its uploads count against the rate limits of the user who owns it, so more accounts do not raise the limits.

An owner can convert a team key into a service account from the team page. The token already stored in CI keeps working, including one that starts with team_. The plaintext of a converted key was never stored, so it cannot be shown again. If the owner already has a service account with that name, the key is left as it is.

Team keys

A team key still installs and publishes as one team, so existing CI keeps working with no change. Prefer a service account when a job needs packages from more than one team.

Owners create keys on the team page. Give each system its own key, like one for GitHub Actions and another for Codemagic, so you can revoke one without touching the others. Each key has a name, a role and an expiry.

Role What it can do
Reader Install the team's packages.
Writer Install them, publish new versions and change package options. New packages it publishes start in its team.

A key expires after 7, 30, 60 or 90 days, after a year, or never. Its token starts with team_ and is shown once, when the key is created. Only a SHA-256 hash of it is stored, so a lost key can't be shown again, only replaced.

Pub takes it like any other token. Save it as a secret called PRIVATE_PUB_TOKEN and add it to pub before pub get.

Terminal
dart pub token add https://private-pub.dev --env-var PRIVATE_PUB_TOKEN

A key only works for its own team, even when the owner who created it is on others. It belongs to the team, so it keeps working after they leave.

The team page lists each key with who created it, when it expires and when it was last used, which updates at most every 5 minutes. Revoking a key stops it right away. A request with an expired key gets a 401 that says when it expired and where to get a new one.

A key's uploads count against the rate limits of whoever created it, so more keys don't mean more uploads. The package requirements list the limits.

Pub keeps one token for each server, so a project can only use one token at a time. A service account token covers every team it belongs to. A team key only covers its own team. The setup guide shows where the token goes in CI.

Who can see a package

Anyone signed in who isn't on a package's team gets a 404 for it, on the site and over the API, as if it didn't exist.

They're never sent the pub.dev package of the same name instead. That's why a new private package can't take a name pub.dev already has.

When a new package's name is too close to a private package on a team you're not on, the error doesn't say which package it is.

To depend on another team's package, you have to be on that team too, at least as a reader. A team key only sees its own team. A service account sees every team it's on. A package you can't see counts as missing when you publish.

pub.dev's uploader endpoints answer 403 with a link to the package's team, since the team decides who can publish.

Package activity

Every change to a package is recorded, and the record is only added to.

That is publishing the package, publishing a version, moving it to another team, retracting or restoring a version, changing its options, and deleting it when its team is deleted. Each record says who did it, the package, the version when there is one, the team and when. A change made with a team key names the key and the person who created it. A change made with a service account names the account and the user who owns it.

Anyone on the package's team can read that history, including readers, on its Activity tab. The team page lists the same records for the packages currently on that team. It doesn't list another team's packages, and a package that has moved is listed only for its new team. Deleting a team removes those pages. The deletion records stay in the database.

Deleting a team

Owners delete a team at the bottom of its page. It can't be undone.

Deleting a team deletes all of its packages, with every version and their docs and scores, and all of its team keys. Service accounts lose this team and keep their other teams and tokens. Nothing can install those packages after that, and anything that uses one of the keys, like CI, stops working right away. Everyone on the team loses access, its pending invites are dropped, and it stops inviting new accounts from an allowed email domain.

To keep a package, move it to another team first. The page asks you to type the team's name to make sure you mean it. Personal teams can't be deleted.

Deleting a team on Premium cancels its subscription right away, and the rest of the period isn't refunded. If Stripe can't cancel it, nothing is deleted.

A deleted package's name is free again, so anyone can publish a new package with it. A request for it gets the pub.dev package of the same name instead, if there is one. Take it out of everything that depends on it before you delete the team.