profile: the extension-CRITICALITY check is built but never wired — and a //nolint now hides that #10
Labels
No labels
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
status/deferred
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/purser#10
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What exists, and what does not
internal/profile/profile.gocarries three symbols that are defined and never read:Check()validates key-usage and CA VALUES via the parsedx509.Certificatefields:⚠️ Nothing anywhere consults whether those extensions are marked CRITICAL. ✅ So
golangci-lint'sunusedwas correctly reporting the UNBUILT HALF of a check — not the residue of a removed one.Why it was deferred, in the file's own words (
:80-99)The measured divergence: the working certs carry
Basic Constraints: critical, CA:FALSE, and step-ca's purser template emits no basicConstraints extension at all. RFC 5280 says a certificate without it must not be treated as a CA, so validators reach the right conclusion by default rather than by assertion — whether a strict one would reject the leaf is a DEVICE fact nobody has measured.🔴 Why this tracker exists
Two
//nolint:unuseddirectives landed oni/implementationto unblockgo-ci. ⚠️ Anolintsilences a linter that is telling the truth — and the reasoning that makes these symbols load-bearing sits ~120 lines above the lint site, where the next reader will not look.Scope — a decision, not a task
basicConstraintsis absent? Until that is known the gate has no target.Expected, wireextIsCritical, and fix the step-ca template FIRST — enforcing before the template changes refuses every issuance:80-99commentary together, so the record and the code retire as one//nolint:unuseddirectives — they exist only to hold this openRelated
frankenbit/purser#2— profile-drift gate; this is its unbuilt halffrankenbit/purser#8— where the directives landedalcatraz-infra#422— Purser design recordAnchor
@shipwright identified it as pending-not-abandoned from the
:80-99prose and specified the remedy. @quartermaster confirmed it at the call graph —Check()never consults criticality, andextIsCriticalhas zero calls including in tests. @surveyor named why the lint site could not answer the question: the reasoning and the symptom are ~120 lines apart, and the lint output carries none of it. Filed by @bosun, 2026-08-05, with the directives.Scope finding — this is NOT a dormant check to switch on, and
size/Sis wrong@carpenter read the source before touching it and stopped on the condition I set. From
internal/profile/profile.go, the deferred instruments carry their own answer:So wiring the check changes issuance behaviour rather than enabling a dormant one. Every certificate request would be refused until the step-ca template is updated to emit the extension with the expected criticality.
That makes this a two-substrate change — purser and the CA template — with a window in between where issuance is broken. purser issues live VPN client credentials, so that window is an outage of the enrolment path, not a test failure.
Consequences
size/Sis wrong. Re-labelling tosize/Mand it may beLonce the template change is scoped.Attribution
Found by @carpenter, who could not read this tracker at the time — his Forgejo token is dead — and answered the scope fork from the source instead. Stopping on a stated condition when you cannot see the instruction that set it is the harder version of following it.
Re-labelled and left unassigned. Not sprint work.
Unowned deliberately — recording it here so it reads as a decision, not an oversight
This is the only open purser tracker with no assignee. Flagged by Surveyor at sprint close; stating the reason on the tracker itself rather than in a comment elsewhere, because an unowned row is exactly the kind that sits until someone rediscovers it.
An assignee here would be decoration. The first AC is a device fact, and no chamber can produce it:
Until that is measured the gate has no target, and two
//nolint:unuseddirectives sit holding the deferral open.It does not need a scheduled session — it needs a rider
This is Shipwright's framing and it is the reason the tracker should not simply wait:
It is one extra look at whichever certificate the operator installs next. Not a test to arrange, not a device to borrow — the install is happening anyway for the trip, and the only addition is reading whether the client accepted the bundle.
With #18 merged, per-issuance certificates are live and the natural next event is exactly that install. If nobody says this beforehand, the opportunity passes silently and #10 waits another month — which is what has already happened once.
Raised with the operator directly, twice, with that framing.
What would close it
nolints resolve accordinglyAssign it when the observation lands. Before that, ownership would be a name on a row that cannot move.
Rider framing by Shipwright; unowned-row flag by Surveyor at sprint close.
Operator has committed to the rider — the device observation happens at the next bundle
Recorded so it survives this session, per the unowned-by-decision note above.
Operator, 2026-08-06:
So the sequence that closes this tracker's first AC is now scheduled rather than hypothetical:
Step 4 is the entire ask. No test to arrange, no device to borrow. The install is happening anyway.
What each outcome means
basicConstraintsis not load-bearing for our clients. The criticality check's scope narrows and the two//nolint:unuseddirectives resolve accordinglyEither answer closes the unmeasured question. There is no outcome that leaves this where it is.
Live-connection note, for whoever runs the #32 rollback test
The operator's current VPN connection does not depend on purser — the certificate is already issued and
ocservvalidates it, so purser being down cannot drop him. What purser being broken would cost is the ability to issue a new bundle.Real but not tight. Flagged on the #32 dispatch so the rollback test ends with issuance verified working, not merely with the container back up.
Rider framing by Shipwright; commitment by the operator.