# Publishing your first package (/docs/guides/publishing-a-package)



This guide walks through publishing a package from scratch. By the end you'll have a package on the LPM.dev Registry that anyone you grant access to can `lpm install`. We'll cover quality gates, secret scanning, and provenance — the things you can't see from the command name alone.

## 1. Authenticate [#1-authenticate]

```bash
lpm login
```

Opens your browser, you authenticate against the LPM.dev Registry, LPM CLI captures the redirect token and stores it in your OS keychain. You only do this once per machine.

```bash
lpm whoami
```

Confirms the identity, plan tier, and any orgs you belong to.

## 2. Create the package [#2-create-the-package]

```bash
mkdir my-pkg && cd my-pkg
lpm init --lpm
```

`lpm init --lpm` walks you through `owner` / `name` / `version` / `description`. The published name will be `@lpm.dev/<owner>.<name>` — pick an `owner` you've claimed on the LPM.dev Registry (or one your org owns). Plain `lpm init` asks for the target first; choose **LPM.dev Registry package** for this guide.

For a non-interactive bootstrap (CI scripts):

```bash
lpm init --lpm -y --owner <owner> --name <name>
```

Plain `lpm init -y` is still valid and defaults to an LPM.dev Registry package, but the explicit flags avoid leaving placeholder owner/name values in automation.

## 3. Write your code [#3-write-your-code]

Build out the package — `src/` (or wherever), `dist/` (your build output), README, license. The defaults `lpm init` writes to `package.json` (substituting the owner/name you supplied):

```json
{
  "name": "@lpm.dev/<owner>.<name>",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "type": "module",
  "license": "MIT",
  "files": ["dist"],
  "packageManager": "lpm@<version>"
}
```

It also writes an `AGENTS.md` package-manager hint so coding agents in the repo use `lpm install`, `lpm add`, and `lpm run`.

Tighten `files` to whatever you actually want shipped. Anything not listed (README, LICENSE, package.json) is included by default per npm convention.

## 4. Preview the publish [#4-preview-the-publish]

```bash
lpm publish --dry-run
```

Runs every step except the upload: packs the tarball, scans for hardcoded secrets, computes the quality score. If anything's wrong, this is where you find out.

```bash
lpm publish --check
```

Even shorter — only runs the quality report. Useful as a pre-commit hook.

## 5. Set a quality floor (optional) [#5-set-a-quality-floor-optional]

```bash
lpm publish --min-score 80
```

Aborts the publish if the quality score is below 80. Pairs well with CI gates — your team can't ship a package missing a README, types, or a license without explicit override.

The score is computed from readme presence/length, license declaration, types coverage, test signals, and maintenance heuristics. See [`lpm quality`](/docs/packages/quality) to view the report for an already-published LPM.dev Registry package.

## 6. Publish [#6-publish]

```bash
lpm publish
```

Pack → secret scan → quality report → confirmation prompt → upload. Skip the prompt with `-y` (good in CI).

The package starts in **private** distribution mode — only you (and anyone you grant access to) can install it. To make it discoverable or paid, flip the distribution mode in the LPM.dev Registry dashboard:

| Mode                  | What it means                                                                                    |
| --------------------- | ------------------------------------------------------------------------------------------------ |
| **Private** (default) | Only the publisher and granted users can install. Metadata is not public.                        |
| **Pool**              | Metadata is public. Installs gated to pool subscribers. Publisher earns a share of pool revenue. |
| **Marketplace**       | Installs require a license purchase.                                                             |

Mode changes are **irreversible** — going public is a one-way door. See [Distribution mode](/docs/reference/glossary#distribution-mode).

## 7. (Optional) Provenance [#7-optional-provenance]

In CI with an audience-`sigstore` OIDC token — GitHub Actions or GitLab CI:

```bash
lpm publish --provenance
lpm publish --npm --provenance
```

Generates a Sigstore-signed attestation that proves which workflow built the tarball and which commit it came from. With `--npm`, LPM CLI attaches npm's expected `{name}-{version}.sigstore` bundle in the publish payload. Consumers can verify provenance via the audit pipeline or the registry UI.

`--provenance` fails loud (non-zero exit) if no usable Sigstore-audience token is found or if Sigstore signing fails — it never silently publishes without provenance. Useful as a hard gate in security-critical packages.

On GitHub Actions, enable `permissions: id-token: write` on the job. On GitLab CI, mint `SIGSTORE_ID_TOKEN` via the `id_tokens` block with `aud: sigstore`. Other platforms aren't supported.

For npm-compatible targets, provenance requires public access. LPM CLI's npm default is public for scoped and unscoped packages. If repo config enables provenance but a one-off publish should skip it, pass `--no-provenance`.

## 8. Publishing elsewhere [#8-publishing-elsewhere]

`lpm publish` defaults to the LPM.dev Registry. To target other registries:

```bash
lpm publish --npm                                 # registry.npmjs.org
lpm publish --github                              # GitHub Packages
lpm publish --gitlab                              # GitLab Packages
lpm publish --publish-registry https://r.example.com   # custom npm-compatible
```

You must have auth for the selected target first: `lpm login --npm` opens npm's browser login, while `lpm login --github` / `--gitlab` can use existing `gh` / `glab` auth without storing a copied token. Custom registries still use `lpm login --login-registry <URL> --token <T>`. For multiple targets in one go, set `lpm.json > publish.registries` — see [`lpm.json`](/docs/reference/lpm-json#publish).

## Common pitfalls [#common-pitfalls]

* **`init -y` defaults aren't directory-derived.** Under `-y`, the target is the LPM.dev Registry, owner is your `lpm whoami` profile username (or the literal `"username"` if offline / not logged in), and &#x2A;*name is literally `"package"`** (not your directory's name). The published shape becomes `@lpm.dev/<your-username>.package` — pass `--owner` / `--name` or edit before publishing.
* **Quality score below 80 by default = no readme, no license, no types.** Easy fix; usually a 5-minute task.
* **Distribution mode is irreversible.** Pool / marketplace are one-way doors. Stay private until you're sure.
* **Generated provenance only works in OIDC-capable CI.** Don't pass `--provenance` from a developer laptop. For npm-compatible targets with a pre-generated bundle, use `--provenance-file <PATH>`.

## See also [#see-also]

* [`lpm publish`](/docs/packages/publish) — full flag reference
* [`lpm quality`](/docs/packages/quality) — what the score covers
* [`lpm init`](/docs/packages/init) — manifest scaffolding
* [Authentication](/docs/infra/authentication) — token management
