# Set up Private Pub

> Pub needs your token on every machine that publishes or installs your packages. Start with the basic setup, then follow the guides for your editor, your agents and your CI.

This is the markdown version of https://private-pub.dev/docs/setup.

## Basic setup

Do this once on each computer that publishes or installs your packages. Flutter reads the same tokens as Dart, so it covers flutter pub too.

### 1. Get your token

Sign in at https://private-pub.dev, then open your account page at https://private-pub.dev/account and create a token. It stays hidden on the account page until you show it, and you can come back later to copy it again.

### 2. Add it to pub

Run this and paste the token when pub asks for it. Pub saves it and sends it with every request to this server.

```sh
dart pub token add https://private-pub.dev
```

Run `dart pub token list` to check that it was saved.

### 3. Publish a package

Set `publish_to` in the package's pubspec.yaml, then publish it as usual. It starts in your personal team, so only its members can see it, unless an optional `private_pub.team` names another team you can publish to. The server checks every upload the way pub.dev does, and the [package requirements](https://private-pub.dev/docs/package-requirements.md) list what it looks for.

pubspec.yaml:

```yaml
name: my_package
description: A short sentence about what the package does.
version: 1.0.0
publish_to: https://private-pub.dev
```

```sh
dart pub publish
```

### 4. Install packages

Choose how your apps get packages. Both ways use the token you added.

**Everything through this server.** Set PUB_HOSTED_URL and every dependency comes from here, your packages and pub.dev packages alike. Add it to your shell profile, such as ~/.zshrc, so every terminal gets it.

```sh
export PUB_HOSTED_URL=https://private-pub.dev
```

**Only the dependencies you choose.** Mark the dependencies that should come from this server with hosted, and the rest keep coming straight from pub.dev.

pubspec.yaml:

```yaml
dependencies:
  my_package:
    hosted: https://private-pub.dev
    version: ^1.0.0
```

To add a private package from the command line, name this server with it. In a Flutter app, run `flutter pub add` instead.

```sh
dart pub add 'my_package:{"hosted":"https://private-pub.dev"}'
```

> If a project uses the mirror, set `PUB_HOSTED_URL` everywhere pub runs for it, including your editor, your agents and CI. The guides below show how.

## VS Code

The Dart and Flutter extensions run pub with your SDK, so they use the token from the basic setup. Dependencies marked with hosted need nothing else.

### Use the mirror

If you export `PUB_HOSTED_URL` in your shell profile, restart VS Code so it picks the variable up. To set it for one project and share it with your team, put it in the workspace settings instead. The file holds no secrets, so commit it.

.vscode/settings.json:

```json
{
  "dart.env": {
    "PUB_HOSTED_URL": "https://private-pub.dev"
  },
  "terminal.integrated.env.osx": {
    "PUB_HOSTED_URL": "https://private-pub.dev"
  },
  "terminal.integrated.env.linux": {
    "PUB_HOSTED_URL": "https://private-pub.dev"
  },
  "terminal.integrated.env.windows": {
    "PUB_HOSTED_URL": "https://private-pub.dev"
  }
}
```

`dart.env` covers the pub commands the extensions run, like the one after you save pubspec.yaml. The terminal settings cover the commands you type in the integrated terminal, once you trust the workspace.

> If you added the token with `--env-var`, pub reads it from that variable every time it runs, so VS Code has to see the variable too. On your own computer, a plain `dart pub token add` is simpler.

## Claude Code and other agents

Agents run pub in a shell, so they need what your terminal needs: your token, and PUB_HOSTED_URL if you use the mirror. They also need to know which packages live here.

### Claude Code on your computer

Claude Code runs commands as you, so it uses the token you already added.

To use the mirror, set `PUB_HOSTED_URL` in the project's settings. Every command Claude runs gets it, and cloud sessions read the same file.

With sandboxing on, Claude Code asks before a command reaches a new domain. Allow `private-pub.dev` up front so pub never waits on the prompt.

.claude/settings.json:

```json
{
  "env": {
    "PUB_HOSTED_URL": "https://private-pub.dev"
  },
  "sandbox": {
    "network": {
      "allowedDomains": ["private-pub.dev"]
    }
  }
}
```

### Claude Code on the web

Cloud sessions start on a fresh machine without Dart, Flutter or your token. Set them up in the cloud environment settings at [claude.ai/code](https://claude.ai/code).

On Pro and Max plans, add an API credential with `private-pub.dev` under Allowed websites and your token as the `Authorization` header, with the `Bearer` prefix. The proxy adds it to every request to this server, so the token never reaches the session and pub needs no setup.

On other plans, add `PRIVATE_PUB_TOKEN` under Environment variables. Anyone who uses the environment can read it, so keep it in a personal environment, not a shared one. Set Network access to Custom, add `private-pub.dev` to Allowed domains, and check Also include default list of common package managers so pub.dev still works.

Either way, install Dart or Flutter in the Setup script. With the environment variable, add the token to pub there too.

Setup script:

```sh
#!/bin/bash
set -e
# Install Dart or Flutter here, then:
dart pub token add https://private-pub.dev --env-var PRIVATE_PUB_TOKEN
```

### Tell the agent where packages come from

Unless the project uses the mirror, a plain `dart pub add` looks for packages on pub.dev. Say where private packages live in CLAUDE.md, or in AGENTS.md for other agents.

CLAUDE.md:

````md
## Private packages

Private packages are hosted on https://private-pub.dev. Add one with:

```sh
dart pub add 'package_name:{"hosted":"https://private-pub.dev"}'
```

See https://private-pub.dev/llms.txt for how to use the server.
````

### Other agents and cloud machines

Codex, Cursor, GitHub Codespaces and anything else that runs on a machine of its own work the same way. Store a service account token as a secret environment variable and add it to pub with `--env-var` before pub get. A personal token is fine on a machine only you use. The machine also needs to reach `private-pub.dev`.

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

## GitHub Actions

In CI, pub reads the token from an environment variable, so it never goes in the repository.

### 1. Add the token as a secret

In the repository on GitHub, open Settings, then Secrets and variables, then Actions. Add a repository secret called `PRIVATE_PUB_TOKEN` with a service account token.

> Create a service account and add it to every team the job needs, as a reader to install or a writer to publish. One token reaches all of those teams, so CI never needs your own token, which can publish every package you can. A [team key](https://private-pub.dev/docs/teams.md#team-keys) still works for a single team. The [teams guide](https://private-pub.dev/docs/teams.md#service-accounts) covers service accounts.

### 2. Add it to pub before pub get

Set the secret for the whole job, not just one step, because pub reads it every time it runs.

.github/workflows/ci.yaml:

```yaml
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      PRIVATE_PUB_TOKEN: ${{ secrets.PRIVATE_PUB_TOKEN }}
      PUB_HOSTED_URL: https://private-pub.dev # Only if you use the mirror
    steps:
      - uses: actions/checkout@v6
      - uses: dart-lang/setup-dart@v1
      - run: dart pub token add https://private-pub.dev --env-var PRIVATE_PUB_TOKEN
      - run: dart pub get
      - run: dart test
```

For a Flutter app, set up Flutter instead and run the flutter commands:

.github/workflows/ci.yaml:

```yaml
    steps:
      - uses: actions/checkout@v6
      - uses: subosito/flutter-action@v2
        with:
          channel: stable
      - run: dart pub token add https://private-pub.dev --env-var PRIVATE_PUB_TOKEN
      - run: flutter pub get
      - run: flutter test
```

> Workflows run for pull requests from forks don't get your secrets, and neither do the ones Dependabot starts. If Dependabot updates this project, add `PRIVATE_PUB_TOKEN` as a Dependabot secret too.

> Very Good Workflows run pub get before their `setup` input, so they can't add the token in time. Use a job like this one for projects with private dependencies.

### 3. Publish from CI

Run `dart pub publish --force`, which skips the confirmation prompt. This workflow publishes whenever you push a version tag.

.github/workflows/publish.yaml:

```yaml
name: Publish

on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    env:
      PRIVATE_PUB_TOKEN: ${{ secrets.PRIVATE_PUB_TOKEN }}
    steps:
      - uses: actions/checkout@v6
      - uses: dart-lang/setup-dart@v1
      - run: dart pub token add https://private-pub.dev --env-var PRIVATE_PUB_TOKEN
      - run: dart pub publish --force
```

## Codemagic

Codemagic keeps the token in a secret environment variable, and your workflow adds it to pub before getting packages.

### 1. Add the token to Codemagic

Open the app's settings in Codemagic and go to the Environment variables tab. Add a variable named `PRIVATE_PUB_TOKEN` with a service account token as its value, put it in a group called `private_pub`, select Secret and click Add.

### 2. Add it to pub before getting packages

With codemagic.yaml, import the group and add the token before `flutter pub get`.

Codemagic runs each script in a shell of its own, so set `PUB_HOSTED_URL` under vars, not with export.

codemagic.yaml:

```yaml
workflows:
  build:
    name: Build
    environment:
      flutter: stable
      groups:
        - private_pub
      vars:
        PUB_HOSTED_URL: https://private-pub.dev # Only if you use the mirror
    scripts:
      - name: Add the Private Pub token
        script: dart pub token add https://private-pub.dev --env-var PRIVATE_PUB_TOKEN
      - name: Get packages
        script: flutter pub get
      - name: Test
        script: flutter test
```

With the Workflow Editor, add the token command to the Post-clone script, which runs before Codemagic installs dependencies.

To use the mirror, also add `PUB_HOSTED_URL` on the Environment variables tab.

Post-clone script:

```sh
#!/bin/sh
set -e
dart pub token add https://private-pub.dev --env-var PRIVATE_PUB_TOKEN
```
