Mounting the embedded IAM took the console down for anyone logged out, and the
mechanism was not IAM: the shared *zip.App was handed to all 107 subsystems, so
any one of them could call app.Use() and gate every route in the process. Blast
radius was a slice position in apps.Wire(). The point fix stopped that one
subsystem; this stops the class.
MountFunc now takes cloud.Router, not *zip.App. Routes register exactly as
before — absolute paths, same specificity — but the two doors to app-wide
middleware, app.Use(mw) and app.Group("/x", mw), are bounded to the prefixes the
subsystem declares. An empty MountSpec.Prefixes means the /v1/<name> convention
every subsystem already follows, so only a subsystem that gates something else
has to name it: IAM exports its two subtrees as iam.Prefixes (the same list that
registers its routes and serves its 503 — one list, three uses), and zen names
/v1 because its model claim genuinely spans it. Middleware outside those
prefixes is not installed and fails the mount, so the binary refuses to boot
half-gated rather than serving with a stranger's gate on.
Global is the one way back to the bare app, and it is spelled out in Wire() and
frozen by TestWireOrderMatchesFrozen, so a new grant cannot arrive as a quiet
field on one line of a 128-entry literal. Seven hold it — ai, agent, authz,
commerce, licensing, metrics, o11y — and every one is a linked module whose own
Mount still takes *zip.App. None of them installs middleware today (measured);
each stops needing Global when its module takes cloud.Router.
Fiber() is promoted onto the scoped Router rather than granting four more
Globals for four read-only uses (in-process dispatch and the route table).
transport.SetApp takes the *fiber.App for the same reason: the package is
imported by cloud, so it cannot name cloud.Router, and the engine was all the
dispatch ever needed.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
28 lines
1.1 KiB
Go
28 lines
1.1 KiB
Go
package cloud_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/hanzoai/cloud"
|
|
)
|
|
|
|
// TestMountFunc_IsTheSubsystemSignature pins the registry's mount contract: the
|
|
// signature every subsystem exports IS a cloud.MountFunc, checked by the compiler.
|
|
//
|
|
// This file used to test cloud.Typed, the adapter that took a MountFunc's `any`
|
|
// app and asserted it back to *zip.App. Both of its tests went with it, and
|
|
// neither is a loss:
|
|
//
|
|
// - "Typed recovers the *zip.App" only ever proved the adapter handed through
|
|
// the value it was given. MountFunc now names *zip.App, so there is no
|
|
// recovery step left to get wrong.
|
|
// - "Typed fails closed on a wrong type" can no longer be written: passing
|
|
// "not-a-zip-app" to a MountFunc is a compile error, so the runtime branch it
|
|
// exercised does not exist. A test asserting a wrong type is rejected is
|
|
// precisely what a type already is.
|
|
//
|
|
// What remains is the only claim worth making, and the build enforces it.
|
|
func TestMountFunc_IsTheSubsystemSignature(t *testing.T) {
|
|
var _ cloud.MountFunc = func(cloud.Router, cloud.Deps) error { return nil }
|
|
}
|