docs(license): correct offline grace ladder to match code (7d ceiling, read-only not fail-closed) - #416
Merged
Conversation
P6-E12-W4-S4-T2 unified FailOpenHardTTL from a separate 14-day value to
GraceHardThreshold (7 days), but the offline-mode docs and the
simulate-offline help text were never updated. They still describe a
ladder the CLI has not implemented since that change.
The wiki page was wrong in three separate ways, not one:
- warning starts at 72h, not 7 days (GraceSoftThreshold)
- the ceiling is 7 days, not 14 (GraceHardThreshold)
- past the ceiling the CLI goes READ-ONLY (CanProceed true,
WriteAllowed false), it does not refuse to run
It also documented three environment variables that do not exist in the
codebase (NSELF_LICENSE_OFFLINE_MAX_DAYS, NSELF_LICENSE_CACHE_PATH,
NSELF_LICENSE_OFFLINE) and an air-gap flow built on two of them. The real
cache override is LICENSE_CACHE_PATH at ~/.cache/nself/license.json, and
the real air-gap path is license export/import.
Rewrites the page against the code, separates the two clocks that were
conflated (cache age vs server-reported expiry, the latter carrying its
own 30-day PostExpiryGraceWindow), and records why the 7-day ceiling
deliberately does not widen. Also corrects simulate-offline's examples,
which used 7 and 14 as the warning and hard-stop days when those now land
in the silent and read-only bands respectively.
No threshold is changed; this is documentation catching up to code.
…only bundleEntitledFromGrace requires WriteAllowed, so a bundle install past the 7-day ceiling is refused rather than degraded. Without this the wiki page and web's licensing/offline page read as contradicting each other on the same threshold.
The rewrite asserted no env var widens the grace window. That is wrong: NSELF_LICENSE_FAIL_OPEN=1 takes the unbounded bundleEntitledFromCache branch (checker.go:84-87), removing the age ceiling entirely for CI and air-gap installs. Documents it with the production warning, and states precisely that it switches branch rather than retuning the thresholds.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The defect
P6-E12-W4-S4-T2unifiedFailOpenHardTTLfrom a separate 14-day value toGraceHardThreshold(7 days). The code comment records the change:The docs never followed.
license-verification.mdstill describes the pre-changeladder, so we currently tell paying customers they have 14 days offline when the
CLI cuts them to read-only at 7. That is a support incident waiting to happen, not
a cosmetic drift.
What was wrong
The page was wrong in three independent ways:
GraceSoftThreshold)CanProceed: true, WriteAllowed: false— read-only, still runsIt additionally documented configuration that does not exist anywhere in the
codebase —
NSELF_LICENSE_OFFLINE_MAX_DAYS,NSELF_LICENSE_CACHE_PATHandNSELF_LICENSE_OFFLINEall return zero grep hits — along with an air-gapped flow(
~/.nself/license/offline.key,NSELF_LICENSE_OFFLINE=1) built on two of them.The real cache override is
LICENSE_CACHE_PATH, defaulting to~/.cache/nself/license.json, and the real air-gap mechanism isnself license export/nself license import, which already exist.What this PR does
internal/license/grace.go, with the real states(
valid/grace_soft/grace_hard) and the actual user-facing strings.versus server-reported expiry, which has its own independent 30-day
PostExpiryGraceWindow. Refreshing the cache does not clear an expiry lapse, andthe old page gave no way to tell those apart.
validation sends only the license key with no per-machine identifier, so the cache
is a bare copyable credential and every extra day multiplies that exposure. This
reasoning lived only in a code comment.
simulate-offlineas the way to exercise each band.It also fixes that command's own
--help, which listed7 # Trigger warning bannerand
14 # Trigger hard stop. Under the current thresholds 7 days is past thewarning band and 14 is deep into read-only, so both examples demonstrated the wrong
state. Now 1 / 5 / 10, which land in silent / warning / read-only.
Not in scope
No threshold is changed. This is documentation catching up to code. The 72h and
7d constants are deliberate and well-argued in
grace.go; if anything here reads asa case for widening the ceiling, that is a separate decision with a security
tradeoff attached.
Verification
go build ./cmd/...— successgo vet ./cmd/commands/— no issuesdefinition; the three fabricated vars confirmed absent by grep across
*.go.Follow-up (separate PRs)
The same stale ladder was copied into two other repos and is being corrected
alongside this:
web/org/src/content/docs/licensing/offline.mdx(which carries asourceWiki:pointer at this very file) andplugins-pro/.github/docs/licensing/(
entitlements.md,ping-api.md, both still claiming 14 days).