Repository navigation
moduleauthority: mint through the real host gateway in CI, type the 412 delegation-missing refusal, document organization refusals #35
Description
What we are trying to do
The SDK's tests should go red when the host's contract moves, not when a module first mints in production. They also shouldn't let a caller misread a refusal it can act on.
Why now
#30 regenerated gen/ from host module-saas-starter v0.0.94. That regeneration surfaced a break that every test in this repo had missed.
moduleauthoritystill sentX-Codefly-Module-Secreton all three mints.- The v0.0.94 gateway reads only
Authorization: Bearer <projected ServiceAccount token>(module/services/auth-gateway/code/gateway_modules.go:99-122). Its own comment: "No header carrying a secret is read any more." - The suite stayed green because
moduleauthority/host_fake_test.gois a hand-written fake that still accepted the old header.
#30 fixed the break. The proof came from a one-off throwaway test that served the real v0.0.94 gateway handlers (newGatewayHarness, gw.ServeHTTP) to the SDK's real client. Nothing in this repo runs that check, so the next drift will be invisible again.
Asks
1. Real-gateway conformance in CI
Mint through the host's real gateway handlers at the tag recorded in SOURCE.txt, not only through the fake.
- CI checks out the host at that tag (gen-drift already does).
- It serves the real
auth-gatewayhandlers overhttptest, with the accounts leg faked the way the host's own gateway tests fake it. - It runs the three
moduleauthoritymints (work context, operation context, source operation context) through the SDK's real client.
Assert:
- the request is accepted;
- the fields accounts receives (
service_account_token,organization_id,binding,source_id); - the refusal mapping, from accounts code → gateway status → SDK error;
- that the legacy header, a missing bearer, a duplicate bearer, or a non-Bearer scheme is refused.
The throwaway version of this test, real_gateway_review_test.go.txt, is in the #30 review packet. Keep the fake for edge cases the real handlers cannot be driven into. Where the fake still diverges from the real gateway, either align it or document why. The review listed these divergences:
- D1: secret plus bearer: the fake returns 401, the real gateway 200.
- D3: the prefix pattern differs.
- D4: check order differs.
- D5: the fake returns a 403 on work-context, which the real gateway never sends.
2. Type the source-mint 412 (review finding F1, minor, inherited from #34)
moduleauthority/source_context.go:11-17,104-120turns a source-mint 412 into a bareBrokerError.ErrDelegationMissingis deprecated, anderrors.Is(err, ErrDelegationMissing)is false.- The host's
module/WORK_CONTEXTS.mdat v0.0.94 tells consumers to treat 412 as "a person must reconnect this source". - The gateway sends 412 on this path only for
DELEGATION_MISSING(any other FailedPrecondition becomes 502). So map on the status alone, exactly as 403 →ErrDelegationInvaliddoes today. That needs no body read and keeps fix(moduleauthority): a refusal stays a refusal, and a broker error never echoes the bearer #34's rule. - Un-deprecate
ErrDelegationMissingand updateREADME.mdandCHANGELOG.md.
3. Say what an organization refusal looks like (review finding F2, minor)
- A shared-principal module that omits
OrganizationID, or a pinned module that names another organization, is refused by accounts (module_identity.go:266-275). The gateway turns that into 401 on the work mint (403 on the operation mint), so the SDK reportsErrInvalidCredentials. - The
ErrInvalidCredentialsdoc comment (client.go:43-49) lists only token, workload and build causes. State the organization cause there and in the README'sOrganizationIDparagraph. - Optionally, refuse a non-canonical UUID in
New(client.go:252-276). TodayACMEpasses construction and fails at the first mint as an untyped 400.
Not verified
- Accounts' real mint handlers were not run in the feat(datasource)!: invoke declared operations with host receipts #30 review; they need a Kubernetes TokenReview and a database. The accounts leg was faked the way the host's gateway tests fake it.
- F2's 401 mapping was exercised against the real gateway. The accounts-side refusal cause was read from the code, not run.
Activity
antoinetoussaint-byte commentedon Oct 11, 2026
This is out of scope for #36 and I have not filed anything; I'm recording it here as asked.
The real-gateway conformance test is coupled to host internals. module-saas-starter's auth-gateway is
package main, and its test harness is an unexported helper. So #36'sconformance/hostgatewaytest is compiled into that package bygo test -overlayand binds to four unexported names:newGatewayHarness(*Gateway).authz(*ExtAuthz).backendConn(*ExtAuthz).internalTokenA host rename turns the job red at compile time, so it fails loudly, never silently. The fix that removes the coupling belongs in the host: an importable, exported gateway test harness (for example a non-
mainpackage exposing the handler with an injectable accounts connection). Once that exists, this SDK could depend on it instead of overlaying a file.Two smaller observations from the same work:
sdk-go/workcontext(b91ce91 → b3fb40d) and severalgolang.org/x/*modules for the conformance build only.run.shprints this list on every run.NewGatewayAuthoritytargets/modules/_authority, which the v0.0.94 gateway does not serve.antoinetoussaint-byte commentedon Oct 13, 2026
lazybox has finished working on this.
claude(Opus 5.5) held it from 2026-10-11 23:42 UTC until 2026-10-20 01:11 UTC. Theworkinglabel has been removed; this comment is the record.claim detail