diff --git a/content/docs/infra/login.mdx b/content/docs/infra/login.mdx index d6ecbf4f..b84c141b 100644 --- a/content/docs/infra/login.mdx +++ b/content/docs/infra/login.mdx @@ -205,6 +205,28 @@ lpm whoami --json The command checks the selected LPM-compatible registry account. Output includes plan, MFA, Pool access, usage, scopes, credential sources, expiry warnings, and storage health. +The output rounds usage past a limit up, so that it shows as past the limit. + +On Hobby, the tarballs of packages whose sales exempt them do not count toward the limit. The content that the registry derives from those packages still counts: READMEs, API docs, LLM context, skill content, and for a Swift package, its source archive, raw `Package.swift`, and manifest bundle. The output shows the storage that the limit counts, and shows the exempt tarballs on a separate line. + +Past the storage limit, the output says what publishing does, as the registry reports it: + +| Report | What publishing does | +| --- | --- | +| Billed | LPM.dev bills storage past the included size per GiB a month, rounded up. The output also says this at the limit, where the next publish starts the billing. | +| Paused | Billing is not active, so the registry refuses publishes past the limit until billing is active again. While Stripe tries a failed payment again, storage that is already past the limit is still billed. It is not billed while the subscription is paused or after it ended. Past the limit, the output says which of these applies, if the registry reports it. | +| Blocked | Nothing bills storage past the limit, so the registry refuses publishes to your personal scope past it. On Hobby, this continues until you upgrade, or delete enough storage to go below the limit. On a Pro plan without a subscription, which staff can grant, this continues while the storage stays past the limit. | + +At the storage limit, any publish goes past it. The output says so where the registry refuses publishes. At or past the private-package limit, the output says that the registry refuses new private packages in your personal scope. + +Some registries do not report what publishing does past the limit, or the storage that the limit counts. The output then compares the whole storage, exempt tarballs included. On Hobby, at or past the limit, the output says that this registry refuses publishes to your personal scope. Packages whose sales exempt them are the exception. Every LPM.dev Registry refuses Hobby storage past its limit. For other plans, the output says only that the storage is past the limit. + +JSON output reports what the registry sent. A field is `null` if the registry does not send it: + +- `storage_overage`: what publishing does past the limit +- `storage_excess_billed`: whether storage already past the limit is billed meanwhile +- `usage.counted_storage_bytes`: the storage the limit counts + Human output masks the email address. JSON includes the full address. The third-party registry list reports credential availability; it does not test each credential with its provider. diff --git a/content/docs/infra/store.mdx b/content/docs/infra/store.mdx index 5861aff3..05c9a496 100644 --- a/content/docs/infra/store.mdx +++ b/content/docs/infra/store.mdx @@ -289,7 +289,7 @@ lpm store clean --json { "success": true, "removed_bytes": 1048576, - "removed": "1.0 MB", + "removed": "1 MiB", "path": "/Users/you/.lpm/store/v1", "v1_path": "/Users/you/.lpm/store/v1", "v2_path": "/Users/you/.lpm/store/v2", diff --git a/content/docs/packages/publish.mdx b/content/docs/packages/publish.mdx index bd4169dc..4df37a48 100644 --- a/content/docs/packages/publish.mdx +++ b/content/docs/packages/publish.mdx @@ -243,7 +243,37 @@ Run [`lpm install`](/docs/packages/install) before publication. Missing bundles, | Encoded archive path | 32 KiB | | Path components, including `package/` | 256 | -The `package/` prefix leaves 255 components for a project-relative path. Target-specific rewrites and bundled dependencies use the same limits. LPM.dev Registry accepts uploads up to 100 MiB. Other registries can impose smaller limits. +The `package/` prefix leaves 255 components for a project-relative path. Target-specific rewrites and bundled dependencies use the same limits. Other registries can impose smaller limits. + +LPM.dev Registry accepts compressed tarballs up to 100 MiB. It accepts publish requests up to 150 MiB, which hold the tarball in base64 with the metadata of the package. If a publish includes LPM.dev Registry, LPM CLI refuses a larger tarball before it publishes to any target. A `--provenance` publish adds its Sigstore bundle to the request two times, about 10 KB each. As a result, LPM CLI keeps 1 MiB of the limit free for it. + +## When LPM.dev Registry refuses a publish + +A publish to LPM.dev Registry and another target first asks LPM.dev Registry, and sends the size of the tarball. If LPM.dev Registry refuses the publish for good, for permission, payment, or size (HTTP 402, 403, or 413), the publish stops before any target publishes. These refusals are for: + +- The storage, private-package limit, or billing of the account +- The 2FA rule or IP allowlist of an organization +- A scope that the account does not own +- The size of the tarball +- The permission of the token + +Each target handles other answers at its own upload. One of these answers is a version that LPM.dev Registry already has, for example after a partial publish. LPM.dev Registry gives the `VERSION_EXISTS` code, or an older LPM.dev Registry gives only its message. The LPM.dev target then reports that answer and does not upload. The other targets publish. + +Each target also handles these answers at its own upload: + +- A request that LPM.dev Registry cannot read +- An unknown package +- A rate limit +- A server error +- An answer that LPM CLI cannot read + +After a rate limit, a timeout, or a server error, LPM CLI tries the request again first. + +Before any target publishes, the same publish then completes the second factor of LPM.dev Registry, unless LPM.dev Registry already has the version. If 2FA is on, it asks for the code. With `--json` and no `--otp`, it publishes to no target. A malformed `--otp`, or a token that LPM.dev Registry refuses, also stops it at this point. + +If LPM CLI cannot read the account, for example while LPM.dev Registry is unavailable, the other targets still publish. The LPM.dev target tries again and reports its own result. + +This first question counts the tarball. At the upload, LPM.dev Registry also counts the content that it derives from the tarball: the README, skill content, and for a Swift package, its source archive, raw `Package.swift`, and manifest bundle. As a result, LPM.dev Registry can still refuse a publish at its upload if the publish is within that much of the limit. API docs and LLM context, which it derives later, also count toward storage. `--dry-run` asks the same question and fails on any refusal. ## Credentials and OTP diff --git a/content/docs/packages/release.mdx b/content/docs/packages/release.mdx index 4d196220..1d73bdaa 100644 --- a/content/docs/packages/release.mdx +++ b/content/docs/packages/release.mdx @@ -189,6 +189,12 @@ lpm release publish --all --lpm --otp 123456 --yes For LPM.dev Registry, `--otp` supplies the same six-digit authenticator code to each package. Use a fresh code before it expires. +Members publish one at a time. The release prepares and examines each member when it reaches that member. If a member is refused before or at its upload, the release stops after the members that already published. This applies to the size of the member, a secret that its files hold, and the storage of LPM.dev Registry. + +If LPM.dev Registry refuses a member in its preflight, the release stops. The error follows the status of the answer, as for any failed request. In `--json` output, its `error_code` is `auth_required`, `forbidden`, `not_found`, `rate_limited`, or `http`, with the message from LPM.dev Registry. The release skips a version that LPM.dev Registry already has. LPM.dev Registry says so with the `VERSION_EXISTS` code, or an older LPM.dev Registry says so with its message. + +`--dry-run` prepares every member and examines the version on each target. It does not upload. It does not ask LPM.dev Registry about storage or the private-package limit, which the upload of each member adds to. As a result, a release that passes a dry run can still stop before it is complete. + ### Lifecycle scripts Publishing can run these scripts from each selected member: diff --git a/content/docs/reference/exit-codes.mdx b/content/docs/reference/exit-codes.mdx index 51ee60c3..f9d6917d 100644 --- a/content/docs/reference/exit-codes.mdx +++ b/content/docs/reference/exit-codes.mdx @@ -105,7 +105,7 @@ A failed operation does not always emit the shared error envelope: | [lpm exec](/docs/dev/exec), [lpm dlx](/docs/dev/dlx), and direct source-file execution | Child I/O passes through. `--json` does not add a CLI result envelope. | | [lpm audit](/docs/packages/audit), [lpm tidy](/docs/packages/tidy), and [lpm doctor](/docs/infra/doctor) | Command report plus a status that reflects the selected failure conditions. | -Tool results omit successful child logs and truncate included failed streams at 10 MB each. +Tool results omit successful child logs and truncate included failed streams at 10 MiB each. An `exit_code: null` with an `error` means the member could not start or prepare its tool. A command can complete its checks but report findings. For example, doctor can return a completed report with `success: true` and exit `1`. diff --git a/content/docs/reference/terminal-output.mdx b/content/docs/reference/terminal-output.mdx index b07a62a8..30384cb2 100644 --- a/content/docs/reference/terminal-output.mdx +++ b/content/docs/reference/terminal-output.mdx @@ -62,7 +62,7 @@ JSON serialization preserves the semantic values of included strings. It escapes It does not remove terminal commands from those strings. This does not guarantee a complete copy of child output. A command can omit successful logs or truncate captured failures. -For example, workspace tool results include failed output with a 10 MB limit per stream. +For example, workspace tool results include failed output with a 10 MiB limit per stream. Parse JSON before you use its fields. If your program later displays a parsed string in a terminal, sanitize that string at display time. diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 88beb5d8..9811a1ab 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -233,7 +233,7 @@ }, "infra/store.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-09-20T03:12:34+01:00" + "modified": "2026-10-12T13:47:31+01:00" }, "infra/token-rotate.mdx": { "published": "2026-06-16T23:33:51+01:00", @@ -353,7 +353,7 @@ }, "packages/publish.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-09-18T23:56:31+01:00" + "modified": "2026-10-13T21:58:31+01:00" }, "packages/quality.mdx": { "published": "2026-05-01T09:47:04+01:00", @@ -369,7 +369,7 @@ }, "packages/release.mdx": { "published": "2026-07-03T10:46:26+01:00", - "modified": "2026-09-17T21:19:46+01:00" + "modified": "2026-10-13T21:58:31+01:00" }, "packages/remove.mdx": { "published": "2026-05-01T09:47:04+01:00", @@ -501,7 +501,7 @@ }, "reference/terminal-output.mdx": { "published": "2026-07-22T19:51:43+01:00", - "modified": "2026-09-20T19:20:27+01:00" + "modified": "2026-10-11T15:38:30+01:00" }, "registries.mdx": { "published": "2026-05-01T13:51:26+01:00",