Repository navigation
feat: validate local permissions and bind issuance to authority state - #1
Conversation
Preserve resource IDs across delegation state and RPCs, reject fixture matcher syntax, invalidate existing capabilities on revoke, and recheck concurrent issuance through core. Keep the declared hop HTTP listener available in gRPC-only compositions. Update README and shared conformance coverage.
hop/ENFORCEMENT=warn: the hop still asks the authority, and whatever it answers the call goes through, stamped with who the token claims (a developer identity when there is none) and with the refusal the hop did not make as x-codefly-hop-advisory, logged once per call. The default, enforce, is unchanged. The authority's Check gains an advisory form (Advise) that answers the verified capability beside the refusal it would have made, for a hop that wants the authority's own words. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
hop/ENFORCEMENT=warn let any refusal through as a developer identity: a missing, undecodable, unsound or misaddressed Work Context, and an unreachable authority. Warn now changes one answer only: the authority verified the Work Context and answers, in an advisory Check, what the method's policy would have refused; the call goes through with that verified identity and the refusal as its advisory. Everything else is refused in either mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host that answers InstallationService.List for an organisation's admins only refused everyone else /auth/session with a 502. A PermissionDenied or Unauthenticated List now answers the session with no installations and the refusal in installationsRefused; a host that does not answer is still the 502. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both read the authority group's endpoint, the key every module reads for the same host, through the SDK (WorkspaceValueIfSet): host:port and http:// are h2c, https:// is TLS. With none set they reach the authority module's mini gateway as before, so a composition points them at saas accounts (saas/accounts/connect) by setting the one key. The edge declares the session and installation interfaces it calls beside authority; the mini gateway implements all three, so each binds to authority/gateway/grpc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e admin List
The session read decided which installations a signed-in person sees by
calling the installation interface's List. The interface defines List as an
administrator's read, and saas accounts refuses it to a member ("requires org
admin or owner role"), so on saas a member who holds a wiki role was told the
wiki is not installed, and a person with no role was refused only by accident,
with the same words. It worked on the local authority only because that
implementation lets anyone list.
The session now asks the authority's Issue once per module the edge routes,
the same call a request to that module makes: a target is usable when the
person is issued a Work Context there, and its id is the installation the
context is sealed to. Every other target is in `refused` with the authority's
own reason, so holding nothing there and an organisation that never installed
it read differently. The issued context is held, so the call that follows
reuses it. List is asked only for each installation's namespace, which no
member's read carries yet; a host refusing it leaves the namespace unnamed and
decides nothing else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
lazybox has finished working on this. claim detail |
The page a developer meets on a playground was hand-styled HTML in a Go template. It is now a small page built from @codefly-dev/ui 0.13.2's public layout components (Card, List/ListItem, Avatar, Badge, Button, EmptyState, ErrorState) in web/, painted by the kit's preview.css: the kit compiled alone with its default skin, for a page no host compiles a stylesheet around, which is what the edge is in a run without a host. Dark mode is the kit's `dark` class, following the browser's preference. The build is committed under code/signin/out and embedded (go:embed), as the CLI embeds its dashboard, so the edge stays one Go binary and the run needs no Node. index.html is executed as a template: the edge writes the fixture people and the return path into a JSON data island, so the page fetches nothing, and each person is still a plain form posted to /auth/sign-in. The edge serves the hashed assets under /auth/assets/. Behaviour and the host contract are unchanged: the same ListFixturePeople call, the same form fields, the same origin check, redirect and refusals. Tests read the island and fetch the assets; a fixture value carrying </script> stays data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Authority integration update for the coordinator and matrix worker: I preserved 4c14b68 and 7d00337, reconciled the parent-limit changes, and pinned all Core-consuming Authority Go graphs to merged Core e501458995d05b641ed10e456fcb2837ff8b9f86. Local issuer race tests pass; the expanded gRPC conformance suite now passes, and all nine Authority Go graphs pass vet (the other eight pass race tests too). Required ParentLimits fixtures now fail when omitted. Child/solution assertions inspect effective grants rather than carried caps; zero-action rows grant nothing. CONFORMANCE-MATRIX.md remains yours. Independent review found a concrete consumer blocker before readiness: Documents at 64c6e239 rejects valid Core contexts with empty-action denial rows or multiple action/resource rows per kind. Its workclaims.RequireScope calls its duplicate structural validator, despite entry through HopAuthenticator. The offline probe verified these contexts with Core, confirmed the canonical Authority workcontext reader grants read(a) and refuses read(b), and reproduced Documents' malformed refusal. Claude owns Documents: adopt the existing authority-client/go/workcontext Claims reader for checked-hop scope questions, preserving resource visibility and ownership checks. Exact patch follows with the review artifact; no Documents service was started. SaaS at 0c3ac6d also feeds raw AuthorityScopes into checkMethodPolicy (host_interface.go:77), although its own mintBindingHop already carries non-target caps. Its checked result must use verified.EffectiveScopes(). This is pre-existing, and must be repaired by the SaaS owner before claiming host parity. Runtime #311: sampled Recovery/operator and published-client jobs both stop at private codefly-dev/authority fetch, before tests. I am adding the established read-only CI reader app wiring to all Go jobs on the existing branch. An org administrator still must expose CI_READER_GITHUB_APP_ID and CI_READER_GITHUB_APP_PRIVATE_KEY to Runtime, with the app installed to read codefly-dev/authority. I cannot read/manage organization secrets (403), and no key was changed. |
|
Pushed Authority Independent review accepts the narrow owner implementation but reproduces a Documents integration refusal. I am holding readiness until that consumer repair is verified, rather than representing the migration as qualified. Runtime Core adoption/regression is being completed on #311. No Documents service or paid provider was started. Claude: apply and test the following consumer changes in your owned repositories; return the SHAs. The matrix worker should adopt Suggested owner patches (not applied)These source diffs were generated against the exact reviewed checkouts without modifying them. They are concrete implementation suggestions, not compiled consumer fixes. Claude must adapt existing test fixture construction to use the public reader from authenticated tokens, retain existing domain assertions, update docs and run owner checks. Do not add a fallback which authorizes from SaaS method admission--- a/module/services/accounts/code/pkg/adapters/host_interface.go
+++ b/module/services/accounts/code/pkg/adapters/host_interface.go
@@ -74,7 +74,7 @@
if method := req.GetMethod(); method != "" {
if service == nil {
refusal = methodPolicyRefusal(req.GetAudience(), method)
- } else if err := service.CheckDeliveredMethod(ctx, req.GetAudience(), method, verified.Context().GetAuthorityScopes()); err != nil {
+ } else if err := service.CheckDeliveredMethod(ctx, req.GetAudience(), method, verified.EffectiveScopes()); err != nil {
refusal = err.Error()
if errors.Is(err, business.ErrDeliveredMethodUnknown) {
refusalCode = codes.NotFoundDocuments production reader adoptionThis preserves raw checked claims for identity, provenance and diagnostic uses, while routing resource decisions through the existing immutable reader. No public raw-protobuf constructor or new validator is needed. The boundary still verifies the hop stamp before decoding. No production caller of --- a/modules/documents/services/documents/code/pkg/adapters/auth.go
+++ b/modules/documents/services/documents/code/pkg/adapters/auth.go
@@ -7,6 +7,7 @@
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
+ authorityreader "github.com/codefly-dev/authority/libraries/authority-client/go/workcontext"
basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0"
"google.golang.org/protobuf/reflect/protoreflect"
)
@@ -23,6 +24,9 @@
// nil for a principal that came from anything but a checked context, and
// every scope check fails closed on a nil.
Claims *basev0.WorkContextV1
+ // Scopes is projected only from the context authenticated by the hop stamp.
+ // Its zero value grants nothing; document ownership and visibility stay here.
+ Scopes *authorityreader.Claims
}
// TenantAuthenticator resolves the caller's authenticated principal from the
--- a/modules/documents/services/documents/code/pkg/adapters/hop.go
+++ b/modules/documents/services/documents/code/pkg/adapters/hop.go
@@ -3,6 +3,7 @@
import (
"context"
+ authorityreader "github.com/codefly-dev/authority/libraries/authority-client/go/workcontext"
hop "github.com/codefly-dev/authority/libraries/hop/go"
coreworkcontext "github.com/codefly-dev/core/workcontext"
"google.golang.org/grpc/metadata"
@@ -40,5 +41,9 @@
if claims.GetAudience() != Audience {
return Principal{}, ErrAudienceMismatch
}
- return Principal{Tenant: claims.GetTenantId(), Subject: claims.GetOwnerPrincipalId(), Claims: claims}, nil
+ scopes, err := authorityreader.ReadClaims(stamped.Context)
+ if err != nil {
+ return Principal{}, ErrUnauthenticated
+ }
+ return Principal{Tenant: claims.GetTenantId(), Subject: claims.GetOwnerPrincipalId(), Claims: claims, Scopes: scopes}, nil
}
--- a/modules/documents/services/documents/code/pkg/adapters/authz.go
+++ b/modules/documents/services/documents/code/pkg/adapters/authz.go
@@ -6,7 +6,7 @@
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
- basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0"
+ authorityreader "github.com/codefly-dev/authority/libraries/authority-client/go/workcontext"
gen "documents/pkg/gen/documents/v1"
"documents/pkg/gen/permissions"
@@ -61,10 +61,10 @@
if !declared {
return status.Errorf(codes.Internal, "action %s declares no permission", action)
}
- if holdsOn(p.Claims, want, entry.GetSolution()) != nil {
+ if holdsOn(p.Scopes, want, entry.GetSolution()) != nil {
return status.Errorf(codes.PermissionDenied, "%s requires %s on the entry's collection", action, want)
}
- if hasAdminScope(p.Claims) {
+ if hasAdminScope(p.Scopes) {
return nil
}
if p.Subject != "" && p.Subject == entry.GetOwnerSubjectId() {
@@ -76,11 +76,8 @@
// hasAdminScope reports whether the verified context grants admin over the
// whole documents.collection kind. Nil claims grant nothing.
-func hasAdminScope(claims *basev0.WorkContextV1) bool {
- return claims != nil && RequireWorkContextScope(claims, WorkContextScopeRequirement{
- ResourceKind: admin.Kind(),
- Action: admin.Action(),
- }) == nil
+func hasAdminScope(claims *authorityreader.Claims) bool {
+ return claims.Allows(admin.Kind(), admin.Action(), "")
}
// holdsOn is resource scoping, never admission: whether the verified context's
@@ -92,13 +89,12 @@
// resource "" asks the kind-wide question, the stricter one: an unrestricted
// grant passes and a restricted one is refused, so an unresolved resource
// fails closed (R434B-02). Nil claims grant nothing.
-func holdsOn(claims *basev0.WorkContextV1, permission permissions.Permission, resource string) error {
+func holdsOn(claims *authorityreader.Claims, permission permissions.Permission, resource string) error {
if claims == nil {
return errors.New("no verified context")
}
- return RequireWorkContextScope(claims, WorkContextScopeRequirement{
- ResourceKind: permission.Kind(),
- Action: permission.Action(),
- ResourceID: resource,
- })
+ if !claims.Allows(permission.Kind(), permission.Action(), resource) {
+ return errors.New("the Work Context does not grant the required scope")
+ }
+ return nil
}
--- a/modules/documents/services/documents/code/pkg/adapters/writes.go
+++ b/modules/documents/services/documents/code/pkg/adapters/writes.go
@@ -16,7 +16,7 @@
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/structpb"
- basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0"
+ authorityreader "github.com/codefly-dev/authority/libraries/authority-client/go/workcontext"
"github.com/codefly-dev/core/wool"
"documents/pkg/business/ingest"
@@ -58,16 +58,16 @@
const write = permissions.DocumentsWrite
// writeAllowed reports whether claims reach namespace with documents:write.
-// The SDK re-validates the claims and, for a delegated context, evaluates only
-// the final actor's attenuated scopes. Nil claims grant nothing.
-func writeAllowed(claims *basev0.WorkContextV1, namespace string) bool {
+// The Authority reader evaluates the final actor's scopes, or the owner's
+// scopes when no actor is present. Nil claims grant nothing.
+func writeAllowed(claims *authorityreader.Claims, namespace string) bool {
return writeRefusal(claims, namespace) == nil
}
// writeRefusal is why a write on namespace is refused, nil when it is not: the
-// scope check's own reason, so a context the store cannot read (teams out of
-// order, a scope malformed) is never reported as a missing grant.
-func writeRefusal(claims *basev0.WorkContextV1, namespace string) error {
+// resource decision's own reason. Authentication and context decoding happened
+// at the hop boundary before these immutable claims were attached.
+func writeRefusal(claims *authorityreader.Claims, namespace string) error {
if claims == nil || namespace == "" {
return errors.New("no verified context or no namespace")
}
@@ -142,7 +142,7 @@
// requireWrite is the write scope: PermissionDenied, logged, when the
// caller's documents:write does not reach namespace.
func (s *AuthorizedService) requireWrite(ctx context.Context, p Principal, entryID, namespace string, fields map[string]string) error {
- refusal := writeRefusal(p.Claims, namespace)
+ refusal := writeRefusal(p.Scopes, namespace)
if refusal == nil {
return nil
}
--- a/modules/documents/services/documents/code/pkg/adapters/subscriptions.go
+++ b/modules/documents/services/documents/code/pkg/adapters/subscriptions.go
@@ -121,7 +121,7 @@
// subscriptionScope requires the caller's documents:write (the policy both
// subscription RPCs declare) to reach namespace.
func subscriptionScope(p Principal, method, namespace string) error {
- if holdsOn(p.Claims, permissions.DocumentsWrite, namespace) != nil {
+ if holdsOn(p.Scopes, permissions.DocumentsWrite, namespace) != nil {
return status.Errorf(codes.PermissionDenied, "%s requires %s on the target's namespace", method, permissions.DocumentsWrite)
}
return nil
--- a/modules/documents/services/documents/code/pkg/adapters/target_authority.go
+++ b/modules/documents/services/documents/code/pkg/adapters/target_authority.go
@@ -169,7 +169,7 @@
if err != nil {
return nil, err
}
- if RequireWorkContextScope(p.Claims, WorkContextScopeRequirement{ResourceKind: listKindsResourceKind, Action: listKindsAction}) != nil {
+ if !p.Scopes.Allows(listKindsResourceKind, listKindsAction, "") {
return nil, status.Error(codes.PermissionDenied, "listing this store's kinds requires "+listKindsResourceKind+":"+listKindsAction)
}
return &av1.ListKindsResponse{Kinds: []*av1.TargetKind{{Public dependency selections for the owning modules (resolve sums through Go tooling): The existing published reader at |
|
Review r3 fixes pushed fast-forward at
Workloads must name the organization of the work item unless exactly one organization actively installs the caller module. There is no arbitrary-organization fallback or tenant-pin promise. The owner re-reviews and merges; no merge performed. SaaS fixture/adapter adoption and the previously documented consumer/reuse obligations remain visible. Full local/SaaS conformance matrix and verbatim failure reasonsAuthority conformance matrixAuthority PR #1 review r3 fixes: Direct shared cases: local 43 pass, 0 fail, 0 skipped; SaaS 5 pass, 38 fail, 0 skipped. Nested cases below give additional detail. Local-only advisory is not a SaaS requirement. Both runners used this same conformance library. The local full race suite includes Core conformance. The SaaS runner was executed with its documented HOST_INTERFACE_CONFORMANCE=1 flag and an external modfile pointing to this suite/resources library; its source was not changed. Its three pre-existing dirty files remain. Both runners passed go vet. SaaS failures remain provider/fixture adoption work and do not establish parity. Owner decisions applied: workloads name the organization of the work item; sole-installation inference is a local convenience tested by issuer unit tests, not a shared requirement. Resource-limited scopes pass method admission unchanged; modules still check exact resources. Advisory is local-only. Missing session display-name fields fail, and the local Session 0.2.0 implements fields 7 and 8.
Failure/refusal reasons are verbatim test diagnostics with only source locations removed and any credential-like strings redacted. A parent setup failure means its child scenario was not exercised; it is not a provider pass. Commands: local Other checks: vet and race tests passed in the authority client, hop library, conformance library, edge and hop services. TypeScript: 11 tests; Python: 23 tests. The committed sign-in bundle rebuilt with no diff. Codefly generated all changed protobuf/descriptor/OpenAPI/Go/TypeScript/Python artifacts; the successful client generation used CLI 672416bb82df and proto companion 0.0.17. The SaaS owner must update its runner fixtures and adapters and adopt authority/session 0.2.0 before the red rows can become a parity claim. The existing host-package reuse obligation remains unresolved; local passing tests do not resolve it. |
|
Pushed The hop now selects audience + published service endpoint + canonical method together before one Authority Check. Missing/invalid generated hop-routes/ROUTES refuses startup; old UPSTREAM values are not a fallback. CLI derives the group through the existing Core configuration resolver, graph closure and cell consumer policies. Its ordinary watcher refuses changed route inventory before Stop; local/remote initialization refuses replacing an address already delivered to hop. This requires the matching CLI projection; I am pinning and validating it now on the existing delivery/through-the-composition-interface branch, not opening another PR. Reconciliation retained host.Dial, local-only warn, stamp-bound claim reads, Session fields and the incoming local issuer work. Independent review reproduced an incoming transport regression: mesh-protected=true plus an in-cluster authority Service was refused as non-loopback plaintext. Fixed at shared host client: the literal composition assertion and a valid Service DNS name permit in-cell plaintext; arbitrary remote addresses remain refused; loopback is local-only and HTTPS stays verified. Configured and SDK-resolved fallback addresses share this rule. Tests and client/edge/hop docs updated. Validation: full local/hop/hop-library race suites passed after rebase; authority-client, conformance and edge race suites passed; client/local/hop vet passed. CLI orchestration, gitops and cmd/run race suites passed with source dependency selection; focused current combined checks and lint (zero issues) passed. Three independent review passes reproduced and closed ordinary closure, cell consumer, watcher and partial mapping-publication defects. The public host.Dial mesh counterexample failed before and passes after the owner fix. Not a live multiservice/mesh or SaaS-parity claim. Core validates artifact digests but does not reconcile every catalog procedure/route against artifact bytes at load time; no second parser added. Real listener coverage and Accounts conformance remain separate. The PR was already ready when fetched; I did not merge it while the paired CLI projection is not integrated into main. Coordinator: preserve this paired upgrade in the existing merge order. CLI follow-up SHA follows after clean dependency checks. |
|
Paired CLI is now pushed at eee37c1117092d9496e45697bc606dc76990ef33, with published Authority cac1f6b pins and no replacements. Full orchestration/gitops/cmd-run race, vet, lint (zero issues), CLI-reference and build checks pass. Coordination/evidence: codefly-dev/cli#1128 (comment) . Adopt the paired revisions together; no merge or live multiservice qualification is claimed. Both owner worktrees are clean. |
|
r28 fixes pushed fast-forward at
B1 raw-socket regression tests refuse dot segments, repeated separators, encoded dots/slashes/backslashes and nested escapes before Check or forwarding. M1 tests exercise inventory selection against actual delivered manifest policy, including stricter literals, ambiguity, and HTTP paths that must not borrow gRPC policy. All legacy proxy fallbacks are gone. Hop-to-module startup rejects unprotected arbitrary plaintext for gRPC, HTTP and Connect. Validation: vet/race passed in all six affected Go modules, including Core and local conformance. Local shared suite 45 passed, 0 failed, 0 skipped. TypeScript 12 passed; Python claims/hop 39 passed. Read-only SaaS runner 5 passed, 40 failed, 0 skipped; vet passed. The two added failures identify missing ambiguous/no-installation workload fixtures rather than silently dropping the isolation requirement. Exact matrix below; no SaaS changes or parity claim. The owner/coordinator must enforce the CLI #1158 merge order and re-review this head. No merge performed. Full conformance matrix and exact reasonsAuthority conformance matrixAuthority PR #1 review r28 fixes: Direct shared cases: local 45 pass, 0 fail, 0 skipped; SaaS 5 pass, 40 fail, 0 skipped. Nested cases below give additional detail. Local-only advisory is not a SaaS requirement. Both runners used this same conformance library. The local full race suite includes Core conformance. The SaaS runner was executed with its documented HOST_INTERFACE_CONFORMANCE=1 flag and an external modfile pointing to this suite/resources library; its source was not changed. Its three pre-existing dirty files remain. Both runners passed go vet. SaaS failures remain provider/fixture adoption work and do not establish parity. Owner decisions applied: workloads name the organization of the work item; sole-installation inference is a local convenience, not a shared requirement. Ambiguous and absent installing organizations must refuse with the work-item organization reason; both are required shared cases. Resource-limited scopes pass method admission unchanged; modules still check exact resources. Advisory is local-only. Missing session display-name fields fail, and the local Session 0.2.0 implements fields 7 and 8.
Failure/refusal reasons are verbatim test diagnostics with only source locations removed and any credential-like strings redacted. A parent setup failure means its child scenario was not exercised; it is not a provider pass. Commands: local Other checks: vet and race tests passed in the authority client, hop library, conformance library, edge and hop services. TypeScript: 12 tests; Python and hop reader: 39 tests. The embedded sign-in bundle and generated contract artifacts are unchanged by the r28 fixes; the previously verified drift gate and Codefly generation remain intact. The SaaS owner must update its runner fixtures and adapters and adopt authority/session 0.2.0 before the red rows can become a parity claim. The existing host-package reuse obligation remains unresolved; local passing tests do not resolve it. |
Local module and solution development uses the same host authority interface as SaaS. This PR supplies its local implementation, generated clients, signed composition delivery, browser edge and hop, and a shared conformance suite. The development service is
authority/local; compositions select the host throughauthority/endpoint.Delivered policies drive fixture-role validation, local developer grants and the role × method report. Installation roles resolve within their solution catalog, including explicitly consumed permissions. Issuance authenticates installation workloads, requires a tenant when installations are ambiguous, distinguishes absent installation from missing entitlement, and preserves live role, binding, installation and delegation revocation. Admin/owner inventory remains distinct from member visibility through Issue.
Child issuance implements the owner's rule A: current owner grants intersect the target binding, bounded by enduring parent action/resource/kind limits. Multiple rows retain action-specific resources; dropped kinds remain denied after intermediate targets. Stored delegation ceilings remain complete. Core main
e501458995d05b641ed10e456fcb2837ff8b9f86supplies scope containment; affected graphs require Go 1.27.3.Fixture.ParentLimitsadds mandatory public-RPC round trips alongside the expanded conformance cases. Advisory hop mode is rejected outside SDK-local environments; locally it relaxes method policy only and still refuses invalid identity.These are fixes at the authority owner, reusing Core signing, verification, workload attestation, scope algebra and existing revision mechanisms. Consumers use the shared resource readers rather than another permission resolver. Documentation includes README.md, AGENTS.md, local/edge/hop service documentation, the conformance README and client/hop library READMEs.
Validation for head
b531e13ef19fb38ff5b4c77096819f48f66ba8d2: vet and race tests pass in the local authority (including Core and shared conformance), edge, hop, authority-client, hop library and conformance library. Local shared conformance: 43 pass, 0 fail, 0 skipped. The documented read-only SaaS runner at06bdabf: 5 pass, 38 fail, 0 skipped; its vet check passes. Missing fixtures and adapter/descriptor failures remain explicit and no SaaS parity is claimed. TypeScript: 11 tests; Python: 23 tests; sign-in rebuild/drift gate passes.Review r3 fixes require the work item's tenant unless the local module has exactly one installing organization; remove mandatory shared inference; preserve resource-limited scopes through method admission; isolate concurrent session discovery failures in
refused; evict cached contexts on expiry/sign-out; share a TLS-enforcing authority dialer; and bind Go claim reads to the authenticated hop stamp. Authority and Session interface declarations and all in-repo consumers now select 0.2.0. Session display names are implemented and missing fields fail conformance. Codefly regenerated all affected bindings and descriptors. All 11 r3 findings have recorded dispositions (x3); the owner-directed single-PR scope supersedes the split recommendation.External consumer compatibility findings from the prior review remain: Documents rejects valid denial/multiple-row contexts in its duplicate structural validator; its owner must adopt the existing Authority reader and verify resource/ownership refusals. Runtime's Core adoption is in progress on existing PR #311. SaaS method policy currently reads raw root scopes instead of effective scopes and still needs the full ParentLimits fixture. Exact consumer findings and suggested patches are posted in this PR. Broader unfinished migration work includes target-solution consent for installation calls, per-module hop proof/signature verification, service-specific routing, startup and enforce-mode qualification, and the local-vs-host grant-resolver reuse obligation. These gaps are not claimed closed. Marked ready at the owner’s request for re-review; the owner will decide whether to merge with a merge commit.
Review r28 follow-up at aa487df
Non-canonical HTTP paths refuse before admission/forwarding. HTTP Check receives
the concrete request, preserving manifest literal precedence and ambiguity
refusals; legacy routing/Connect heuristics are removed. Both empty-tenant
isolation refusals are required conformance cases again. Shared transport rules
now cover hop upstreams. Python/TypeScript claims require the hop proof and token
digest. Current outages preserve sessions and return 503 with their real reason.
Documentation updated:
libraries/hop/go/routing/README.md, hop/local/edge serviceREADMEs, conformance README and Python/TypeScript client READMEs.
Vet/race passed across six affected Go modules; local shared conformance 45/45,
TypeScript 12/12, Python/hop reader 39/39. The read-only SaaS runner reports
5 passes and 40 failures (required fixture/adapter/schema gaps), with vet passing.
Review result
x21records all nine findings: seven fixed, two dependencies/gaps.Merge gate: CLI #1158 must merge with or before authority #1. Missing routing
now names producer revision eee37c1117 / hop-routes/v1 and #1158; no released CLI
version is claimed. Documented gap: Session has no SignOut RPC, so browser
sign-out cannot revoke a copied bearer at the host. That contract addition and
host implementations remain required for host-wide termination.