Publishing
flog publish uses a prepare-then-execute model. It completes the full plan
before the first registry upload, so a manifest, lifecycle, packing, git, or
registry-state failure in any selected package leaves every package
unpublished.
See the
flog publish command summary for
its principal preflight, confirmation, and output flags.
Selection and order#
Without --recursive or --workspace, flog publishes the package containing
the selected directory. Private packages are rejected. --workspace selects by
exact package name, workspace directory, owned file, or workspace path glob;
--recursive separately selects every workspace. Exact and glob selectors
cannot be mixed, and duplicate aliases or overlapping globs are errors. Private
workspace packages matched by broad path globs or a plain recursive selection
are summarized by count in human plans and retain their package name, importer
path, and private reason in JSON plans and summaries. An exact workspace
selector is a direct request and remains an error. A broad selection containing
only private workspaces is also an error.
Selected packages are ordered dependency-first using the workspace graph.
Before running package lifecycles or packing, flog checks each selected exact
name and version in the registry. An existing version is reported as
already-published and skipped without building a local artifact, comparing
integrity, or uploading. This is an identity check, not proof that current local
contents reproduce the published tarball. Changed contents require a new
version; flog never attempts to replace an existing version.
Preflight and lifecycle#
For every selected package, flog first:
- validates every project dependency declaration against
flog.lock, then validates package identity, version, requested tag and access, git state, and workspace selection; - reads registry baselines, classifies exact existing versions, rejects an incoherent workspace release set, and reports newer compatible direct dependency versions as advisory warnings.
For each genuinely unpublished version, flog then:
- runs
prepublishOnly; - packs the package, which runs
prepack,prepare, andpostpack, rewritesworkspace:ranges, and stages the exact tarball to upload; - validates the exact packed dependency declarations against the reviewed lock
after lifecycle changes and
workspace:rewriting; - revalidates the release set against post-lifecycle manifests and the published metadata of skipped versions; and
- displays the ordered publish candidates and a concise plan summary. A human
dry run shows a short terminal-only status line for each candidate while it
builds, then replaces it with the version transition, tag, and access intent.
Detailed package and release-set outcomes remain available with
--json.
Only after the complete plan succeeds and is confirmed does flog upload staged
artifacts sequentially. A successful upload runs publish and postpublish.
Artifacts are never rebuilt between the plan and upload.
Flog records each accepted upload as pending verification and continues with
the next package without waiting for metadata propagation. After uploading,
it verifies accepted versions, artifact integrity, and requested visibility
with fresh registry metadata reads. Checks are sequential in publication order,
and each accepted package gets its own window of up to 60 seconds with bounded
backoff and request counts. The first package that exhausts its window becomes
accepted-pending-verification; later accepted packages become
accepted-not-checked, and verification stops. An upload or pre-upload safety
failure still stops later uploads; already accepted versions are reconciled and
reported separately. Neither pending status is evidence of a failed upload, and
verification never retries an upload. Inspect the registry before recommending
consumption or retrying.
If caller cancellation stops verification instead, later accepted packages are
also accepted-not-checked, but their summary and human output preserve the
cancellation reason rather than claiming that an earlier package timed out.
A source or packed manifest/lock mismatch is always fatal, including dry-run,
and no force or acknowledgement option bypasses it. Lifecycle scripts therefore
cannot make an unreviewed dependency change eligible for upload. A newer
registry version that still satisfies a selected package's declared range is
different: the reviewed lock remains
authoritative, publication continues, and the deterministic warning reports the
locked and available versions with flog upgrade guidance. Exact declarations
and ranges with no newer compatible version produce no warning.
Workspace release sets#
Publication preflight computes a release closure from public workspace
dependencies. If a selected package crosses a compatibility line and a
published dependent's range excludes the proposed version, that dependent must
be included with its own compatibility-line bump. The check propagates through
transitive dependents. For SemVer, compatibility lines are a major version, a
minor version within 0.x, or an individual 0.0.x version.
An already-published dependent is exempt from this backward release obligation
when its version at the requested registry tag has a non-empty npm deprecation
message. A local package.json deprecation flag alone is not authoritative.
The exemption is recorded in JSON release-set diagnostics; it does not excuse a
newly published dependent from compatibility or dependency availability checks.
Registry metadata, including deprecation, is rechecked before uploads.
Flog also rejects packed workspace dependencies whose ranges are not satisfied
by an existing registry version or an earlier package in the publication plan.
It never selects packages or edits versions automatically. Use
--allow-incomplete-release-set only to accept a reviewed exception; the
override remains visible in human plans; every affected edge remains in JSON
reports. --yes and git overrides do not bypass release-set
validation. Development-only workspace
edges are reported in JSON but do not require publication. Human-readable plans
lead with blockers and proposed remedies, list only publish candidates, and
summarize already-published and private packages by count. --json preserves
every informational edge and skipped package for auditing or tooling.
See workspace release sets for compatibility-line rules, dependency-chain examples, availability checks, and override behavior.
Immediately before every upload, flog reads that package's registry metadata
again. It aborts if the prepared version appeared, if latest changed, or if a
registry-reported access value changed after preflight. Registry baselines used
by release-set analysis are also checked before the first mutation and between
uploads. When an OTP challenge
occurs, the same check is repeated after the OTP is obtained and before the
authenticated retry. A mutating request that starts web authentication is not
automatically replayed; after authentication, flog rechecks metadata and
explicitly retries it. These checks narrow the stale-plan window, but
npm-compatible publication endpoints do not provide a portable conditional
update that atomically compares the old dist-tag or access while publishing a
version. A registry change in the final interval between this read and its
upload can therefore still race; flog does not claim race-free latest or
access safety.
Lifecycle scripts and packages run sequentially. Cancellation during a prompt, web login, OTP request, lifecycle script, or upload prevents subsequent uploads.
Registry authentication#
Real publication requires a credential accepted by the selected npm registry.
Use flog login or configure a usable npm token in npmrc before publishing when
the registry does not return an interactive web-authentication challenge. Flog
does not turn an ordinary unauthorized response into login automatically.
Flog requests web authentication on publication uploads with
npm-auth-type: web and npm-command: publish. When npm returns login and completion URLs, flog prints
the readable login URL. If the registry returns a plain OTP challenge without
those URLs, flog still prompts for an OTP rather than guessing a browser URL.
In a supported interactive terminal, press Enter to open it with the platform
browser, or type anything to continue with the printed link. Browser-launch
failure leaves that manual link available. Redirected input or output and
unsupported or TERM=dumb terminals print the link without prompting. If
redacting a known configured credential changes the login URL and it cannot be
opened safely, flog fails before polling with instructions to authenticate
through the registry and retry. Flog polls only the registry-provided
completion URL; neither credentials nor the completion URL are printed.
A web-authenticated mutating request is never replayed implicitly. The
completion code for a publication challenge is sent as npm-otp on the
revalidated retry, not saved as a new login token. After authentication,
flog revalidates the prepared version, tags, access, and release-set metadata,
then makes one explicit retry. A plain npm EOTP challenge prompts interactive
users for a one-time password; automation may set NPM_CONFIG_OTP. If an
EOTP response also carries web-authentication URLs, those URLs take precedence
over a manual OTP prompt. Cancellation stops polling or prompting and prevents
the explicit retry.
Tags and confirmation#
Stable versions default to latest. A prerelease requires an explicit
non-latest tag, for example:
flog publish --recursive --tag next
--tag latest is rejected for every prerelease. For stable versions, flog also
rejects a latest update whose SemVer precedence is lower than or equal to the
registry's current latest version. An exact version that is already published
is skipped even when it is equal to or older than latest, because skipping it
does not move any tag.
Interactive publication asks for confirmation after displaying the plan.
Automation must deliberately pass --yes; redirected or otherwise
noninteractive input is never treated as confirmation.
Package access#
For a first publication, flog follows npm defaults: unscoped packages are
public, while scoped packages are restricted. Use an explicit access level
when the default is not the intended registry visibility:
flog publish --recursive --access public --tag next
One --access value applies to every selected package. public is valid for
mixed scoped and unscoped workspaces; restricted is rejected for any unscoped
package during initial validation, before lifecycle scripts or registry
requests. For an existing scoped package with no option, flog preserves a known
registry-reported public or restricted value. If the registry does not
report existing visibility, the plan says preserve and flog omits the access
field rather than risk requesting a downgrade to the scoped default.
The npm-compatible upload document carries the requested top-level access
field except for that preserve case. Requested access is included in every
plan entry, confirmation display, JSON summary, and summary file.
A successful upload response proves only that the registry accepted a mutation;
it does not prove that the version is visible or that the registry applied the
access field. Human output therefore records a durable publishing line before
each upload and a separate registry accepted line after its response. Uploads
remain sequential and dependency-first. Flog finishes all candidate uploads
before making any verification read, so metadata propagation never delays a
later upload. Authentication, OTP, and lifecycle output remains separate from
these package lines.
After the upload phase, flog verifies accepted packages sequentially in
publication order. Each package receives its own window of up to 60 seconds and
fresh metadata requests with bounded, spaced retries. A terminal may show one
transient next-retry countdown; redirected output uses sparse durable retry
lines and no terminal control characters. Verification never resends an upload.
Flog reports published only when exact artifact metadata and the observed
public/restricted visibility match the access intent.
An accepted upload with different visibility is
published-access-mismatch; unavailable verification or an unknown prior value
under preserve is published-access-unverified. Both outcomes return an
error, record access intent and observed access separately, and warn that the
accepted publication may be irreversible.
publish and postpublish still run because the registry accepted the package.
If a registry rejects the field, flog reports the ordinary failed upload. If it
accepts but ignores the field, flog never silently substitutes visibility or
reports confirmed success. A confirmed public result for requested restricted
access fails immediately. Authenticated metadata that explicitly reports
restricted access for a requested-public upload also fails immediately; when
that field is absent or consistent, missing anonymous visibility is allowed only
during the bounded propagation window. If the first package to exhaust its
window remains invisible, it becomes accepted-pending-verification and later
accepted packages become accepted-not-checked; they did not time out and were
not proved failed. Upload, cancellation, or postpublish failure stops later
uploads as not-attempted, after which already accepted packages are reconciled
without retrying an upload. Dry runs show the access intent but remain anonymous
and never mutate package visibility.
Dry runs, git checks, and summaries#
--dry-run performs lifecycle and packing for unpublished versions plus all
registry-state and safety checks, but does not upload or authenticate. Anonymous
registry metadata reads are bounded to six concurrent requests. It does
not run lifecycle or packing work for versions classified as already published.
Metadata requests omit configured and session credentials and cannot start web
authentication.
By default the worktree must be clean and the current branch must be master.
main is treated like any other non-canonical branch. --allow-dirty and
--allow-branch override those checks individually. --no-git-checks disables
both. Every active override is shown as a warning in the plan.
--dry-run --json runs without terminal progress and writes one machine-readable
summary, including plan safety warnings, to stdout when complete. A blocked dry
run prints full plan diagnostics to stderr. For a real publish with --json,
the plan is still written to stderr
before mutation and the outcome summary to stdout. Use
--summary <file> to atomically record the same summary in a JSON file,
including failed, published-access-mismatch,
published-access-unverified, accepted-pending-verification,
accepted-not-checked, and not-attempted package statuses after a partial or
unverifiable publication. A rerun identifies already-present
versions from registry metadata before rebuilding only the remaining versions.
After published-access-unverified, inspect the registry before retrying: the
upload may already be irreversible. A safe rerun classifies that exact version
as already-published and continues with remaining packages rather than
uploading it again. In recursive publication, later packages remain
not-attempted only when the upload phase stops before reaching them. A
verification timeout happens after uploads and leaves later accepted packages
accepted-not-checked.
The web-auth, OTP, cancellation, and propagation paths are covered with protocol-level and end-to-end mock registries. They are not a substitute for a live npm canary. Before using a new Flog release for rcompat, obtain explicit operator approval for a disposable scoped public package and verify the actual credential/2FA challenge, public access, metadata propagation, and final summary. Until that gate is completed, live npm publication remains an outstanding release risk.