Repository navigation
fix!: the authority is asked about the operation dispatched, never the decoded path (#75 review) - #76
Merged
Merged
Conversation
…e decoded path Review fixes for #75 (r1): - F1 (blocker, security) and F2: wrapRequest asked the authority about r.Method+" "+r.URL.Path, the decoded path, while the mux dispatches on the escaped one, so /pages/secret%2Fhistory ran /pages/{id} under the /pages/{id}/history row; and a subtree beside {id} matched two rows, which the authority refuses as ambiguous. mountOperations now hands wrapRequest the dispatched operation's own row, verb+" "+servedRoute(path), which the authority finds by exact name. - F4: InterfaceInfo.BasePath is deleted. The CLI prefixes every row with it while the edge forwards on /v1/<id> and the runtime checks routes as served, so no non-empty value matched a request. - F5: InvalidArgument/OutOfRange from the authority's Check is 400 with the authority's reason (ErrRequestInvalid), not 503 "try again". - F6: a relayed stream's first message is bounded by the method's Timeout again. - F3 is recorded in the CHANGELOG and README: closes when authority#1 merges with a merge commit and the runtime re-pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fix-forward for the independent review of #75 (r1). One objective: the route the authority checks is the operation the runtime runs, and nothing #75 removed is silently lost.
Findings and fixes
wrapRequestasked the authority aboutr.Method+" "+r.URL.Path, the decoded path. Go's ServeMux dispatches on the escaped path, soGET /v1/page/pages/secret%2Fhistoryran the/pages/{id}operation (Requirespage.page:read) but was checked against the/pages/{id}/historyrow (AnyCheckedCaller). Fix, as the reviewer proposed:mountOperationsgiveswrapRequestthe dispatched operation's own row,method + " " + servedRoute(path). The local authority'sadmitMethodlooks upm.Methods[method]exactly before it tries any template match (authority/visionstate.go:202), so the row is found by name.TestAnEncodedSlashIsCheckedAsTheOperationItRunsis the reviewer's reproduction, over real HTTP throughboot(). A caller holding only the history row gets 403 for/pages/secret%2Fhistory, the page handler does not run, and the authority is asked aboutGET /v1/page/pages/{id}. With the old line put back the test fails:200 {"ran":"page-handler"}.TestOverlappingRoutesAreEachCheckedAgainstTheirOwnRowdeclares a subtree/v1/page/pages/and/v1/page/pages/{id}. Each request is checked against exactly its own row and served. With the old line put back it fails:matches more than one declared route. The stand-in authority now models the real one: exact row first, then template matching with{name=**}, and ambiguity refused.authority/vision. Not re-pinned, as asked. Recorded in the CHANGELOG and in the README's dependency-pins note: this closes when authority#1 merges with a merge commit (so both commits stay reachable frommain) and this runtime re-pins to that merge or a tag. No runtime release is cut before then.InterfaceInfo.BasePath. Chosen: delete the field. That is the strongest form of "refused by name": a backend still setting it fails to compile, namingBasePath. Why not check routes under the base path: the authority edge forwards a solution's requests on/v1/<id>unchanged (authority/visionedge.go:126), and the passthrough is mounted at/v1/<id>too. A request therefore never arrives under/solutions/<id>, and no non-empty base path can match one. Checking with it would only make the rows and the checks agree on a path nothing serves.excel-extract(BasePath: "/solutions/excel-extract", on main and onauthority/wiki-on-the-authority). It pins solution-runtime-go v0.0.13 and keeps working unchanged. When it moves to this (breaking) runtime, it has to carry/v1/excel-extractin its operation paths anyway, as wiki and proposals did when they dropped their base path. The compile error points straight at that change.wikion main still sets one, but its authority branch has already removed it.PassthroughOperationsreturning paths relative to the solution's route. Ten core-solutions tests use it to validate declarations, its doc already says the paths are relative, andInterfaceArtifactis the function that publishes them prefixed. Changing its signature is a separate objective.refusalStatusnow mapsInvalidArgument/OutOfRangeto 400.viewer()returns them asErrRequestInvalidwith the authority's message, which is answered 400 on a handler route andinvalid_argumenton a relayed call. An issue refused that way becomes aWorkContextRefusalof 400 carrying its reason.Unavailable/DeadlineExceededstay 503. Test:TestAMalformedRequestIsRefusedWithTheAuthoritysReason.relayStreamnow cuts the module's stream off at the method'sTimeoutif no first message has arrived, using a timer that cancels a derived stream context, asStreamFirstAnswerWithindid. After the first message, onlyMaxStreamDurationapplies. Test: added toTestAStreamEndsAsTheModuleOrItsBoundsSay. A silent module is cut off at 200 ms with "did not start its stream within 200ms". With the timer disabled, the test waits out the full 1-minute duration and fails.What was reused
servedRoute, the function that already spells every published row. The check now uses the same spelling the artifact publishes, so there is one spelling.admitMethod). No route matcher was added to the runtime.refusalStatus/ sentinel-error pattern for F5.StreamFirstAnswerWithindesign (AfterFunc plus a late flag) for F6.Classification
Fix at the owner for F1, F2, F4, F5 and F6. F3 is a recorded open gap whose fix belongs to the authority's merge; it is deliberately not re-pinned here.
Checks
go vet ./...: ok.go test -race -count=1 ./...: ok (129 s).gofmt -l .: clean.No release (the release agent is holding releases until this merges).
🤖 Generated with Claude Code