Repository navigation
feat: add a canonical string for nudging a site to check in - #11
Merged
Merged
Conversation
Everywhere else in this protocol the connector calls out and the platform answers. CanonicalNudge is the first thing pointing the other way. It exists because of a wait nobody could shorten from the platform side. A backup requested in Manager is a queued row and nothing more - the site collects it on its next claim, up to five minutes away with cron and, on a site whose scheduler runs off ordinary web traffic, however long it takes for somebody to visit. The nudge carries no instruction, and that is the whole design. Four lines: prefix, site, timestamp, nonce. There is no field for a job type, a command, a capability, a path or a destination, so a site receiving one does exactly what it would have done on its own a few minutes later - make an ordinary signed claim and find out for itself what is waiting. Every decision that matters is downstream of that claim and untouched here, so the worst a forged or replayed nudge achieves is an early poll. A second kind of nudge would need a second prefix, which is a change to this package and a moved fixture; the vocabulary is held to one word by the wire format rather than by a comment asking nicely. MGR1-NUDGE is a third prefix rather than a reuse of MGR1-RESPONSE because one key signs both, so the prefixes are the only thing keeping their canonical strings from colliding. There is a test asserting a signed response cannot be presented as a nudge or the reverse, rather than trusting two constants to stay different by inspection. Left out deliberately: the request path, because a Craft action URL varies with actionTrigger, headlessMode, omitScriptNameInUrls and subfolder installs and anything in front may rewrite it - signing it would fail on sites behaving perfectly in exchange for no authority. A body hash, because there is no body, and a canonical string with nowhere to put one is a stronger guarantee than hashing an empty string. A connector version, since a nudge travels platform to site; nudgeHeaders() names four headers and requiredRequestHeaders() is untouched, because one list fitting both directions would fit neither. Add-only. No existing schema, canonical string, signature or fixture moved, so a 1.7.1 platform and a 1.7.1 connector are unaffected by taking this.
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.
Adds
CanonicalNudge, the canonical string the platform signs when it asks a site to check in now. Everywhere else in this protocol the connector calls out and the platform answers; this is the first thing pointing the other way.Why
A backup requested in Manager is a queued row and nothing more. The site collects it on its next
jobs/claim— up to five minutes away with cron, and on a site whose scheduler runs off ordinary web traffic, however long it takes for somebody to visit. The screen is correct for that whole time and looks broken.The shape
The nudge carries no instruction, and that is the whole design. No field for a job type, a command, a capability, a path or a destination. A site that receives one does exactly what it would have done on its own a few minutes later: make an ordinary signed claim and find out for itself what is waiting. Every decision that matters — whether a capability is still granted, which recipients an artifact may be sealed to, where an upload may go — is downstream of that claim and untouched by this. The worst a forged, replayed or misdirected nudge achieves is an early poll.
A second kind of nudge would need a second prefix, which is a change to this package and a moved fixture. The vocabulary is held to one word by the wire format rather than by a comment asking nicely.
Why a third prefix
One key signs claim responses and nudges both, so the prefixes are the only thing keeping their canonical strings from colliding — the same reason
MGR1andMGR1-RESPONSEare distinct. There is a test asserting a signed response cannot be presented as a nudge or the reverse, rather than trusting two constants to stay different by inspection.Left out deliberately
actionTrigger,headlessMode,omitScriptNameInUrlsand subfolder installs, and anything in front may rewrite it. Signing it would fail on sites behaving perfectly, in exchange for no authority:siteIdalready says which installation this is for.nudgeHeaders()names four headers;requiredRequestHeaders()is deliberately untouched, because one list fitting both directions would fit neither.Replay protection is the receiver's job and is not specified here beyond providing the timestamp and nonce to do it with.
Compatibility
Add-only. No existing schema, canonical string, signature or fixture moved — the
requestandresponsefixture blocks are byte-identical, and they are the change detector. A 1.7.1 platform and a 1.7.1 connector are unaffected by taking this; what it adds is unused until both sides are updated.Minor bump to 1.8.0: a capability that did not exist before and that somebody can choose to use.
Verification
composer test— 391 passed (2815 assertions), including 6 new signing testscomposer phpstan— no errorsbin/preflight.sh— both invariants suites green