identity is the composer's: one constructor, and no subsystem asserts it
CI/CD / image (push) Failing after 33m28s
CI/CD / gate (push) Successful in 13s
CI/CD / containment (push) Successful in 1m38s
CI/CD / rollout (push) Skipped
CI/CD / reach (push) Skipped
CI/CD / fanout (push) Skipped
CI/CD / receipt (push) Skipped
Hanzo CI/CD / cicd (push) Successful in 12s
CI/CD / image (push) Failing after 33m28s
CI/CD / gate (push) Successful in 13s
CI/CD / containment (push) Successful in 1m38s
CI/CD / rollout (push) Skipped
CI/CD / reach (push) Skipped
CI/CD / fanout (push) Skipped
CI/CD / receipt (push) Skipped
Hanzo CI/CD / cicd (push) Successful in 12s
cloud.App(name, cfg, deps, tools) now builds every program — the host and all 125 plugin programs — and installs the canonical chain through identity in one place. Identify states the order invariant once: the boundary that mints the validated org runs before the enrichment that parks it, and neither exists without the other. Middleware order at the host is proven byte-identical before and after (18 entries), so a refused request is still audited. Every subsystem self-install of the enrichment is deleted — 74 sites across 67 packages. A subsystem asserting identity for itself repeats a claim it cannot check; two of those copies sat on nodes owning no routes, which zip refuses to compose, and that is the outage that took /v1/o11y and /v1/integrations down. The childless spelling is gone everywhere: a gate passed TO Group is installed at the root bounded by its prefix, and the two-step form that slipped past that (a bare Group then Use on it) is collapsed at its last holdouts — referrals' three gates and the audit fixtures. Tests stop rebuilding the composer by hand: each package that mounts on a bare app owns one compose helper, so anonymous cases still refuse and principal-carrying cases reach the handler exactly as production does. Also fixed, found by the constructor's own test: the markdown negotiation replaced the Vary header CORS had written, so every CORS response on every program advertised Vary: Accept — a shared cache could hand one origin's body to a different origin. The later writer is additive now. Census: zero non-test enrichment installs under apps/, zero childless chains. Build and vet clean over the whole tree in the shipping mode.
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"github.com/hanzoai/cloud/apps/sites"
|
||||
"github.com/zap-proto/zip"
|
||||
"github.com/zap-proto/zip/middleware"
|
||||
)
|
||||
|
||||
// App returns an app carrying everything a Hanzo program must carry, in the one
|
||||
// order those parts are correct in. It is the only way to obtain one: a program
|
||||
// mounts its subsystem on what it gets back and never builds a zip.App itself.
|
||||
//
|
||||
// The point is what a caller no longer has the opportunity to forget. Identity is
|
||||
// not an option a program passes, it is a property of the value it receives, so a
|
||||
// program is either holding an app that identifies its callers or it is holding
|
||||
// nothing. Every other member here was equally forgettable and was equally
|
||||
// forgotten: the o11y binary assembled its own app and reached production with no
|
||||
// panic recovery, no request id, no response-header posture, no tracing, no
|
||||
// request log and no typed-op enrichment — a shape nobody chose and nobody could
|
||||
// see, because there was nothing to compare it against.
|
||||
//
|
||||
// WHERE THE EDGE IS. Production runs ingress → gateway → the front door
|
||||
// (cmd/cloud) → this program. The gateway is the public edge and owns rate
|
||||
// limiting for the internet. The front door installs no middleware of its own —
|
||||
// it routes, serves the console, threads operator flags and scopes credentials —
|
||||
// so a program built here is its OWN edge and defends itself. That is why the
|
||||
// browser and flood defenses are here rather than borrowed from a parent. A
|
||||
// program reached over the plane socket instead trusts what its host asserted:
|
||||
// the kernel answers which process is calling, and the boundary's findings travel
|
||||
// with the request.
|
||||
//
|
||||
// name is what the program calls itself in a diagnostic. tools is the MCP surface,
|
||||
// which only a program holding a subsystem list can project — everyone else passes
|
||||
// nil and serves none.
|
||||
func App(name string, cfg *Config, deps Deps, tools zip.Source) *zip.App {
|
||||
app := zip.New(zip.Config{
|
||||
AppName: name,
|
||||
Logger: deps.Logger,
|
||||
ReadBufferSize: cfg.ReadBufferSize,
|
||||
BodyLimit: cfg.BodyLimit,
|
||||
MCP: zip.MCPConfig{Source: tools},
|
||||
// Cloud's refusal renderer, in place of zip's default — which reads only a
|
||||
// *zip.HTTPError and answers 500 for everything else, so a propagated 402
|
||||
// or 403 reached the console as a dead card. See errmap.go.
|
||||
ErrorHandler: ErrorHandler,
|
||||
// Static Server fallback for responses the ProductionHeaders middleware
|
||||
// cannot reach — the transport's own pre-routing errors (431/400) and any
|
||||
// fiber path that bypasses the chain. Set to this deployment's brand so
|
||||
// those bytes read Server: <brand>, never the framework default "zip" or
|
||||
// "fasthttp" (zip>=v1.8.1 propagates this onto the fasthttp transport).
|
||||
// Handled responses are still branded per-Host by ProductionHeaders.
|
||||
ServerHeader: cfg.Brand,
|
||||
})
|
||||
|
||||
// Canonical middleware pipeline. Order matters:
|
||||
// 1. Recover — panic → JSON 500
|
||||
// 2. RequestID — generate / propagate X-Request-Id
|
||||
// 3. Tracing — one OTel SERVER span per /v1/* request, over ZAP
|
||||
// 4. Logger — request-line log
|
||||
// 5. SanitizeIdentity — establish a VALIDATED principal (see Identify)
|
||||
app.Use(middleware.Recover())
|
||||
app.Use(middleware.RequestID())
|
||||
|
||||
// Production response-header posture — the Stripe/Cloudflare/GitHub-grade
|
||||
// signals plus a security floor, from ONE home in the framework so every
|
||||
// service inherits the same wire posture. Registered right after RequestID
|
||||
// (before the site edge and the business chain) so its headers ride out on
|
||||
// every response: success, error, 404, AND the public-site static bytes.
|
||||
// - Server: the white-label brand of the request Host (BrandForHostOK) — a
|
||||
// lux/zoo caller is never served "hanzo" and no response leaks the
|
||||
// framework name; an unmatched Host falls back to this deployment's own
|
||||
// brand (cfg.Brand), never a framework/single-brand default.
|
||||
// - X-Api-Version: the build version (brand-neutral key) for support correlation.
|
||||
// - HSTS + nosniff: the always-safe security floor (no X-Frame-Options/CSP
|
||||
// here — the console SPA owns its own framing rules).
|
||||
// X-Request-Id stays owned by RequestID above; the two compose.
|
||||
app.Use(middleware.ProductionHeaders(middleware.ProductionHeadersConfig{
|
||||
Brand: func(host string) string { b, _ := BrandForHostOK(host); return b },
|
||||
Neutral: cfg.Brand,
|
||||
Version: cfg.Version,
|
||||
HSTS: true,
|
||||
}))
|
||||
|
||||
// Markdown content negotiation. Registered here — outermost of the business
|
||||
// chain, just inside Recover/RequestID — so its post-Continue transform sees
|
||||
// the FINAL response body and re-serializes it via zap-proto/md when the
|
||||
// caller asked for markdown (Accept: text/markdown or ?format=md). JSON stays
|
||||
// the default for machines; cfg.MarkdownDefaultPrefixes lets designated
|
||||
// agent endpoints (/v1/code/, /v1/agents/…) default to markdown. Touches NO
|
||||
// handler and fails safe (a render error leaves the JSON intact). See
|
||||
// middleware_markdown.go.
|
||||
app.Use(MarkdownNegotiation(cfg.MarkdownDefaultPrefixes))
|
||||
|
||||
// Request tracing. Sits right after RequestID (so the span carries the
|
||||
// request_id) and BEFORE identity/audit/billing/handlers, so the whole
|
||||
// authenticated pipeline nests under one span and the span CONTEXT it writes
|
||||
// via SetContext parents every downstream span (agent.run → agent.step →
|
||||
// chat) into a single trace. Spans ship over the SAME global provider installed
|
||||
// by InstallTelemetry, landing in hanzoai/datastore.
|
||||
// Health/readiness/metrics + non-/v1 paths are skipped (see traceable). See
|
||||
// middleware_tracing.go.
|
||||
app.Use(TracingMiddleware())
|
||||
|
||||
app.Use(middleware.Logger(deps.Logger))
|
||||
|
||||
// Public site edge (clients/sites). Installed FIRST — after Recover/RequestID/
|
||||
// Logger, BEFORE SanitizeIdentity + BillingGate — so a request whose Host is a
|
||||
// published-site host (`<slug>.hanzo.app`) is served the site's static bytes
|
||||
// from OUR S3 and returns HERE, never entering the authenticated/billed API
|
||||
// pipeline. A published site is a PUBLIC artifact: no IAM JWT, no balance gate.
|
||||
// For every other Host this middleware calls Continue() and the pipeline below
|
||||
// runs unchanged. The slug→{org,bucket,prefix} resolver is the projects store,
|
||||
// injected at its Mount via sites.SetResolver; until then a site host 404s
|
||||
// honestly. Org isolation (org+prefix come only from the store keyed by the
|
||||
// validated slug; object keys are rooted-clean) lives in clients/sites.
|
||||
// The edge asks the app that owns the store when it is not in this process,
|
||||
// which in production is always: the pod boots ~25 single-app processes, so
|
||||
// the registry projects.Mount writes is nil here. Co-resident still wins with
|
||||
// no hop — currentResolver prefers the in-process one.
|
||||
sites.SetFallbackResolver(planeSites{})
|
||||
app.Use(sites.New(sites.ConfigFromEnv(cfg.Domain), deps.Logger).Middleware())
|
||||
|
||||
// Edge policy — the role this program absorbs because nothing in front of it
|
||||
// installs middleware. Runs BEFORE identity by design:
|
||||
// - EdgeCORS answers the browser OPTIONS preflight (which carries no
|
||||
// credentials) and short-circuits it, so a preflight never reaches auth.
|
||||
// No-op unless CLOUD_CORS_ORIGINS is set (the shared ingress owns CORS on
|
||||
// the recommended rollout — enabling both would double the ACAO header).
|
||||
// - EdgeRateLimit caps an ANONYMOUS per-IP flood before the JWKS/validate/
|
||||
// downstream work it would trigger — the one gap ScopeRateLimit (which keys
|
||||
// on the validated org, below) structurally can't see. Keyed on the
|
||||
// public client IP; in-cluster direct callers (no X-Forwarded-For) are
|
||||
// exempt, matching the standalone gateway's public-only scope. See
|
||||
// middleware_edge.go.
|
||||
app.Use(EdgeCORS(deps.GatewayPolicy))
|
||||
app.Use(EdgeRateLimit(deps.GatewayPolicy))
|
||||
|
||||
Identify(app, cfg)
|
||||
return app
|
||||
}
|
||||
|
||||
// Identify gives an app a trustworthy answer to who is calling, and makes that
|
||||
// answer reachable from every route beneath it. App does this for every program,
|
||||
// which is the only reason it can no longer be skipped.
|
||||
//
|
||||
// The two halves are one function because each is wrong without the other, and
|
||||
// wrong in a way nothing reports. IdentityMiddleware deletes the authority
|
||||
// headers a client sent and re-mints them from a verified IAM token, so it runs
|
||||
// first: what it produces is the only principal in the process anyone may trust.
|
||||
// The enrichment then parks that principal on the request context, which is the
|
||||
// only path by which a typed op reaches it — a zip.Get[In, Out] handler receives a
|
||||
// context and its decoded In and nothing else. Reversed, it parks whatever the
|
||||
// caller claimed for itself. Installed alone, the boundary validates a caller and
|
||||
// then every typed op reads an empty org and refuses that same caller, which
|
||||
// reaches the wire as a 403 from a service behaving exactly as built.
|
||||
//
|
||||
// That last failure is the reason this is a function rather than two lines of
|
||||
// advice. It is what the o11y binary did while assembling its own app, and its
|
||||
// subsystem then compensated from inside its own Mount, on a group node that
|
||||
// owned no routes — a program zip refuses to compose, which is the outage.
|
||||
func Identify(app *zip.App, cfg *Config) {
|
||||
// The identity trust boundary. HIP-0519 says identity is verified once, at the
|
||||
// edge, and that is the shape to reach. It rests on ONE assumption: the gateway
|
||||
// is the only ingress. That assumption does not hold here yet, and the estate's
|
||||
// own red-team probe says so — with this middleware removed, a request carrying
|
||||
// a forged X-Org-Id, X-User-Id and X-User-IsAdmin reads another org's secret
|
||||
// VALUE from the in-cluster KMS listener:
|
||||
//
|
||||
// PROBE (b) forged org + forged X-User-Id + IsAdmin → 200 {"value":"…"}
|
||||
//
|
||||
// So this stays until service listeners are unreachable except through the
|
||||
// gateway. Removing it is a network-policy change first and a code change
|
||||
// second, and doing the code half alone is a cross-tenant secret read.
|
||||
// red_orgscope_isolation_test.go and TestAudit_AnonRequestNotAttributedToForgedOrg
|
||||
// fail the moment it is dropped; they are the gate on that work, not obstacles
|
||||
// to it.
|
||||
app.Use(IdentityMiddleware(cfg))
|
||||
|
||||
// Besides the validated org, this carries the request a proxying subsystem
|
||||
// forwards identity from and the slot a creator writes 201 or 202 into. It must
|
||||
// precede every typed route, because fiber runs middleware in registration
|
||||
// order and one installed after its leaves never runs. A subsystem whose routes
|
||||
// are spread across several top-level nouns owns no single prefix to hang it
|
||||
// on, which is the other reason it belongs to whoever composes the app. See
|
||||
// typed.go.
|
||||
app.Use(Bridge())
|
||||
}
|
||||
+3
-13
@@ -150,19 +150,9 @@ func MountAccount(app cloud.Router, deps cloud.Deps) error {
|
||||
|
||||
// routesAccount wires the specific self-service routes (order 48).
|
||||
func routesAccount(s *cloud.Service[state], app cloud.Router) error {
|
||||
// Bridge FIRST: a typed op receives only a context, so the request facts its
|
||||
// signature drops — here the VALIDATED principal every route resolves its caller
|
||||
// from — reach it by being parked there. fiber runs middleware in registration
|
||||
// order, so this must precede the leaves below. Serve installs one app-wide too
|
||||
// and nesting is harmless (the inner one is what the handler sees); this one is
|
||||
// what makes the subsystem self-sufficient when it is mounted on a bare app,
|
||||
// which is exactly what its own tests do.
|
||||
//
|
||||
// It goes through Use, not Group(prefix, mw): account's routes are spread across
|
||||
// six top-level nouns, so it owns no single prefix to hang a group on — and
|
||||
// Router.Use is the door that fans middleware out over the prefixes the
|
||||
// composition root declared for this subsystem, which is precisely that set.
|
||||
app.Use(cloud.Bridge())
|
||||
// The composer owns cloud.Bridge: the fused host installs it once at its root
|
||||
// and the plugin constructor does the same for a plugin program, so no
|
||||
// subsystem installs it.
|
||||
|
||||
// The typed registrars take the App behind the Router: a typed op is a route
|
||||
// PLUS a registry entry, and the registry lives on the App (scope.go). A
|
||||
|
||||
@@ -255,12 +255,20 @@ func mountApp(t *testing.T, base, clientID, clientSecret string) *zip.App {
|
||||
return mount(t, "hanzo")
|
||||
}
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge:
|
||||
// the program's composer owns it — serve.go at the root of the fused host, the
|
||||
// plugin constructor for a plugin program. In a test the test is the composer, so
|
||||
// it owes the same install; skipping it drives a program where every org-scoped op
|
||||
// answers 403 for a reason production callers never see.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mount mounts the account subsystem on a bare app — exactly what production
|
||||
// registers (account@48). The caller sets the IAM env (IAM_URL / IAM_MINT_CLIENT_*)
|
||||
// before calling.
|
||||
func mount(t *testing.T, brand string) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
deps := cloud.Deps{Logger: luxlog.New("test"), Brand: brand}
|
||||
if err := MountAccount(app, deps); err != nil {
|
||||
t.Fatalf("MountAccount: %v", err)
|
||||
@@ -742,6 +750,7 @@ func TestAccountClaimsNothingUnderIAM(t *testing.T) {
|
||||
t.Setenv("IAM_MINT_CLIENT_SECRET", "s3cr3t")
|
||||
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := MountAccount(app, cloud.Deps{Logger: luxlog.New("test"), Brand: "hanzo"}); err != nil {
|
||||
t.Fatalf("MountAccount: %v", err)
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ func mountAvatar(t *testing.T) (*zip.App, *memVFS, *fakeIAM) {
|
||||
// sources could not enqueue: a framework cap below the app's, surfacing as an
|
||||
// opaque error nobody could act on.
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test"), BodyLimit: edgeBodyLimit})
|
||||
compose(app)
|
||||
deps := cloud.Deps{Logger: luxlog.New("test"), Brand: "hanzo", Domain: "api.hanzo.ai", VFS: vfs}
|
||||
if err := MountAccount(app, deps); err != nil {
|
||||
t.Fatalf("MountAccount: %v", err)
|
||||
|
||||
@@ -49,8 +49,10 @@ var alice = map[string]string{"X-User-Id": "alice", "X-Org-Id": "acme"}
|
||||
func pinApp(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
// MountAccount installs the identity middleware PinBillingSubject relies on; mounting
|
||||
// it keeps the probe on the same trust plane as the real co-resident registration.
|
||||
// compose installs the identity middleware PinBillingSubject relies on; mounting
|
||||
// the real subsystem keeps the probe on the same trust plane as the co-resident
|
||||
// registration.
|
||||
compose(app)
|
||||
if err := MountAccount(app, cloud.Deps{Logger: luxlog.New("test"), Brand: "hanzo"}); err != nil {
|
||||
t.Fatalf("MountAccount: %v", err)
|
||||
}
|
||||
|
||||
+6
-5
@@ -115,11 +115,12 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
func routes(app cloud.Router, s *cloud.Service[core.State]) {
|
||||
o := ops{s: s}
|
||||
z := cloud.ZipApp(app)
|
||||
// The bridge FIRST: fiber runs middleware in registration order, so one installed
|
||||
// after these leaves would never run — and every op below takes the request off the
|
||||
// context it parks. Bounded to admin's own subtree. Serve installs one app-wide too;
|
||||
// nesting is harmless, and this is what makes the surface testable on a bare app.
|
||||
app.Use(cloud.Bridge())
|
||||
// Every op below takes the request off the context, and whoever composes the app
|
||||
// parks it there — at the root, ahead of these leaves, since fiber runs
|
||||
// middleware in registration order. This surface installs none of its own: one
|
||||
// it installed for itself could only hang on a /v1/admin node, and every op
|
||||
// below registers through the root, so that node would carry middleware over an
|
||||
// empty subtree and zip refuses to compose it.
|
||||
|
||||
// Org-scoped panels — AdmitScoped. Cross-tenant reads are impossible for a
|
||||
// non-super caller.
|
||||
|
||||
@@ -39,6 +39,7 @@ func mount(t *testing.T, iamURL, commerceURL, healthURL string) func(method, pat
|
||||
func mountService(t *testing.T, iamURL, commerceURL, healthURL string) (func(method, path string, hdr map[string]string) (*http.Response, []byte), *cloud.Service[core.State], *fiber.App) {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
s := &cloud.Service[core.State]{State: core.State{
|
||||
IAM: iam.New(iamURL),
|
||||
Commerce: commerce.New(commerceURL, "test-token"),
|
||||
@@ -731,6 +732,7 @@ func TestMount_NilGuards(t *testing.T) {
|
||||
t.Error("Mount(nil app) must error")
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{}); err == nil {
|
||||
t.Error("Mount(nil logger) must error")
|
||||
}
|
||||
@@ -747,6 +749,7 @@ func servePlatformEmpty(t *testing.T) {
|
||||
t.Helper()
|
||||
t.Setenv("ZIP_RUNTIME_DIR", t.TempDir())
|
||||
app := zip.New(zip.Config{AppName: "platform"})
|
||||
compose(app)
|
||||
zip.Post[struct{}, plane.Fleet](app, "/platform/fleet",
|
||||
func(context.Context, *struct{}) (*plane.Fleet, error) {
|
||||
return &plane.Fleet{}, nil
|
||||
|
||||
@@ -34,9 +34,11 @@ func mountWithStore(t *testing.T) (*auditstore.Recorder, func(method, path strin
|
||||
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
s := &cloud.Service[core.State]{State: core.State{AdminOrg: "admin", AuditStore: rec}}
|
||||
// Mirror the real mount: the request bridge, then the typed ops. A typed op sees
|
||||
// the caller only through the bridge, so registering routes without it would test
|
||||
// a wiring that cannot exist.
|
||||
// Stand in for the composer: the principal enrichment at the root, then the
|
||||
// typed ops — the order cloud.App gives every production program. A typed op
|
||||
// sees the caller only through what the enrichment parks, and a group at
|
||||
// /v1/admin would be a node of its own with no routes beneath it, which zip
|
||||
// refuses to compose.
|
||||
app.Use(cloud.Bridge())
|
||||
Routes(app, s)
|
||||
fa := app.Fiber()
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/hanzoai/cloud"
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose stands in for the composer. Production programs are built by
|
||||
// cloud.App, which installs the principal enrichment once at the root before
|
||||
// any route; a test that mounts this subsystem on a bare app owns that duty
|
||||
// itself, exactly once, here. A test that sends no identity is unaffected —
|
||||
// with nothing validated there is nothing to park — so anonymous cases still
|
||||
// refuse, and principal-carrying cases reach the handler as they do in
|
||||
// production.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
@@ -29,6 +29,7 @@ func spec(t *testing.T) (map[string]any, []string) {
|
||||
Logger: luxlog.New("test"),
|
||||
OpenAPI: zip.OpenAPIConfig{Title: "cloud", Version: "v1.0.0"},
|
||||
})
|
||||
compose(app)
|
||||
routes(app, &cloud.Service[core.State]{State: core.State{AdminOrg: "admin"}})
|
||||
|
||||
var live []string
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
func ctxWith(t *testing.T, headers map[string]string, fn func(c *zip.Ctx)) {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
app.Get("/probe", func(c *zip.Ctx) error {
|
||||
fn(c)
|
||||
return c.NoContent(204)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package admission
|
||||
|
||||
import (
|
||||
"github.com/hanzoai/cloud"
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose stands in for the composer. Production programs are built by
|
||||
// cloud.App, which installs the principal enrichment once at the root before
|
||||
// any route; a test that mounts this subsystem on a bare app owns that duty
|
||||
// itself, exactly once, here. A test that sends no identity is unaffected —
|
||||
// with nothing validated there is nothing to park — so anonymous cases still
|
||||
// refuse, and principal-carrying cases reach the handler as they do in
|
||||
// production.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
@@ -38,6 +38,7 @@ func gateApp(t *testing.T, approvalStatus string) *zip.App {
|
||||
}, time.Minute)
|
||||
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
app.Use(Enforce(EnforceConfig{WaitlistURL: "https://waitlist.hanzo.ai", Approvals: approvals, Gate: testGate}))
|
||||
app.Get("/*", func(c *zip.Ctx) error { return c.String(200, "ok") })
|
||||
return app
|
||||
@@ -214,6 +215,7 @@ func TestRule_ForwardHeaderApproved_ThroughWithoutLookup(t *testing.T) {
|
||||
// host, so Enforce never gates pre-boot.
|
||||
func TestEnforce_DefaultGate_FailsOpenPreBoot(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
app.Use(Enforce(EnforceConfig{WaitlistURL: "https://waitlist.hanzo.ai",
|
||||
Approvals: newApprovalsWithLookup(func(context.Context, string, string) (string, bool) { return "pending", true }, time.Minute)}))
|
||||
app.Get("/*", func(c *zip.Ctx) error { return c.String(200, "ok") })
|
||||
|
||||
@@ -348,9 +348,11 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
// gated user can still resolve mode.
|
||||
//
|
||||
// A typed op receives only a context, so the request the ?host= default falls
|
||||
// back to has to be parked there. Installed BEFORE the leaf — fiber runs
|
||||
// middleware in registration order, so one installed after it never runs.
|
||||
app.Use(cloud.Bridge())
|
||||
// back to reaches it from that context. Whoever composes the app parks it there,
|
||||
// at the root, ahead of every leaf; this surface installs no middleware of its
|
||||
// own. One that it installed for itself could only hang on a /v1/flags/waitlist
|
||||
// node, and the leaf below registers through the root, so that node would carry
|
||||
// middleware over an empty subtree and zip refuses to compose it.
|
||||
zip.Get(cloud.ZipApp(app), "/v1/flags/waitlist", waitlistOps{}.mode)
|
||||
log.Info("admission gate ready", "services", n)
|
||||
return nil
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
func mountGate(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir(), Brand: "hanzo"}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
+5
-8
@@ -119,14 +119,11 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
// method are projected from. The exception is named at its registration below.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group("/v1/ads")
|
||||
// The Bridge FIRST, on the subtree ads owns: a typed op receives only a
|
||||
// context, so the validated org has to be parked there, and fiber runs
|
||||
// middleware in registration order — one installed after these leaves would
|
||||
// never run. cloud.Listen installs one app-wide too; nesting is harmless (the
|
||||
// inner one is what the handler sees), and having it here is what makes this
|
||||
// package's own tests — which mount on a bare app — exercise the same
|
||||
// tenancy the binary does.
|
||||
g.Use(cloud.Bridge())
|
||||
// A typed op receives only a context, so the validated org it reads is parked
|
||||
// there by cloud.Bridge. This subsystem does not install it: the program's
|
||||
// composer does, once at the root, after the identity check that mints the org
|
||||
// and before any subsystem registers a route — an order only the composer can
|
||||
// hold.
|
||||
|
||||
// Ops are declared ON THE GROUP: every zip.Router is an OpTarget, and the op's
|
||||
// path is the group's prefix composed with the leaf — the identity every
|
||||
|
||||
@@ -35,13 +35,20 @@ var untypedByDesign = map[string]string{
|
||||
"zip can declare a body-tolerant op.",
|
||||
}
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer does, once at the root. In a test
|
||||
// the test IS the composer, so it owes the same install — skipping it does not
|
||||
// test a stricter program, it tests one where every org-scoped op answers 403
|
||||
// for a reason production could never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mountApp mounts the ads surface on a fresh in-memory app with a temp store,
|
||||
// exactly as the unified binary does — and, deliberately, with NO app-wide
|
||||
// cloud.Bridge, so the bridge these ops read their tenant through has to be the
|
||||
// one routes() installs itself.
|
||||
// composed exactly as the unified binary is: cloud.Bridge at the root, the
|
||||
// subsystem's routes beneath it.
|
||||
func mountApp(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -350,14 +350,11 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
// request that carried one. Serve installs one app-wide, which is why serving
|
||||
// was unaffected and only the tests — which Mount onto a bare app — could see
|
||||
// it; a gate whose absence just one door down is invisible in production is the
|
||||
// kind that stays broken. Under Serve a scope bounds this to the prefixes the
|
||||
// manifest declares, and under a bare Mount it is app-wide, which is what the
|
||||
// subsystem's own door honestly is.
|
||||
//
|
||||
// fiber runs middleware in registration order, so it goes above every route
|
||||
// below, group included.
|
||||
app.Use(cloud.Bridge())
|
||||
g := app.Group("/v1/agents")
|
||||
// cloud.Bridge parks the validated org on the context a typed op receives; it
|
||||
// is the composer's install — once at the root of every program — so this
|
||||
// package does not install its own.
|
||||
//
|
||||
// The root of the surface. Declared on the App with its WHOLE path, not on the
|
||||
// group with an empty leaf: joining "/v1/agents" with "" yields "/v1/agents/",
|
||||
// a different path from the one these two have always served.
|
||||
|
||||
@@ -88,6 +88,7 @@ func mountBilled(t *testing.T, commerceURL string, ai types.AIClient) *zip.App {
|
||||
t.Fatalf("metering.New: %v", err)
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
// AIFallbackModel="best" arms the agent runner's failover so the retry/failover
|
||||
// tests exercise the real escalation path; it never fires for a run whose model
|
||||
// answers (or fails non-transiently), so the other billed tests are unaffected.
|
||||
|
||||
@@ -15,6 +15,12 @@ import (
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose installs what the program's composer installs — cloud.Bridge, once at
|
||||
// the app root. A subsystem never installs its own, so a test app owes the same
|
||||
// root install; without it every org-scoped op answers a 403 no production
|
||||
// program would produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mountApp mounts the agents surface with a deterministic fake AI so run() is
|
||||
// exercised end-to-end over HTTP without a real gateway. Pass a nil interface
|
||||
// to exercise the no-inference fail-closed path.
|
||||
@@ -40,6 +46,7 @@ func mountAppDir(t *testing.T, dir string) *zip.App {
|
||||
func mountAppIn(t *testing.T, dir string, ai types.AIClient, defaultModel string) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: dir, AI: ai, AIDefaultModel: defaultModel}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -203,8 +203,7 @@ func toEventView(e Event) eventView {
|
||||
//
|
||||
// The typed ops are declared on the GROUP, so each op's path is the group's
|
||||
// prefix composed with its leaf — the same composition the router does, and the
|
||||
// identity every projection keys on. cloud.Bridge is installed once, at the top
|
||||
// of Mount, ahead of this call.
|
||||
// identity every projection keys on.
|
||||
func mountSessions(s *cloud.Service[state], app cloud.Router) {
|
||||
o := sessionOps{s: s}
|
||||
g := app.Group("/v1/agents")
|
||||
|
||||
@@ -624,12 +624,6 @@ type patchTargetIn struct {
|
||||
// captured as a ref. The static /v1/agents/targets precedes /v1/agents/targets/:id.
|
||||
func mountTargets(s *cloud.Service[state], app cloud.Router) {
|
||||
g := app.Group("/v1/agents")
|
||||
// cloud.Bridge is installed ONCE, at the top of Mount, ahead of every leaf on
|
||||
// this prefix. It used to be installed here, which was too late for the leaves
|
||||
// registered before this call: fiber runs middleware in registration order, so
|
||||
// the sessions and agent-CRUD routes above would have had no org on the context
|
||||
// the moment they became typed ops.
|
||||
//
|
||||
// TYPED ops, declared on the group itself: zip.Get and friends take any
|
||||
// Router since v1.18.0, so the prefix is part of each op's path and every
|
||||
// projection — the document, the MCP tool, the CLI command, the call plane —
|
||||
|
||||
@@ -199,18 +199,10 @@ func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
// A typed op receives ONLY a context, so the validated org has to be PARKED
|
||||
// there — never carried as an In field, which is caller-supplied and would make
|
||||
// a cross-tenant read something the caller asserts for itself. cloud.Bridge
|
||||
// parks it, and it is installed FIRST because fiber runs middleware in
|
||||
// registration order: one installed below a leaf never runs for that leaf.
|
||||
//
|
||||
// On a scoped mount Use installs it once per prefix the subsystem DECLARES
|
||||
// (scope.go), which is why plugin/analytics/main.go now declares all six of
|
||||
// this app's prefixes: with only the /v1/<name> default, the typed reads at
|
||||
// /v1/errors and /v1/insights/* would sit outside every prefix this subsystem
|
||||
// could gate. Serve installs one app-wide too; nesting is harmless, and the
|
||||
// tests mount this subsystem on a bare app with no Serve, so the subsystem's
|
||||
// own install is what makes them pass.
|
||||
app.Use(cloud.Bridge())
|
||||
|
||||
// parks it, and the COMPOSER installs it, not this subsystem: the fused host
|
||||
// once at its root (serve.go), and a plugin program's constructor likewise. An
|
||||
// install here would hang middleware on prefixes with no routes beneath them,
|
||||
// a program zip refuses to compose.
|
||||
o := readOps{s: s}
|
||||
// The read lenses, declared on the GROUP: each op's path is the prefix composed
|
||||
// with its leaf — the same composition the router does, and the identity every
|
||||
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
func liveApp(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("live")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("live")}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ func TestRoutedPostSetIsExactlyTheDoors(t *testing.T) {
|
||||
var posts []string
|
||||
// GetRoutes(true) drops the `use` entries — middleware, which fiber keeps in the
|
||||
// same stack as routes and reports under every method at the prefix it gates.
|
||||
// cloud.Bridge is one of those (routes installs it so a typed op can read the
|
||||
// cloud.Bridge is one of those (compose installs it so a typed op can read the
|
||||
// validated org), and so is every middleware Serve installs app-wide, so an
|
||||
// unfiltered read has never been "the POST surface" in the real binary either. A
|
||||
// middleware is a passthrough, not a door: it dispatches nothing.
|
||||
|
||||
@@ -19,9 +19,16 @@ import (
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge
|
||||
// (routes says why): the program's composer owns it — serve.go in production — so
|
||||
// a test app owes the same install, else every org-scoped op answers 403 for a
|
||||
// reason no composed program has.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func mountApp(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test")}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -83,13 +83,16 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
|
||||
// routes registers the org-scoped audit surface.
|
||||
//
|
||||
// Bridge FIRST and noStore beside it, both installed BEFORE the leaf: fiber runs
|
||||
// middleware in registration order, so one installed after its route never runs.
|
||||
// Bridge parks the validated org, which is the only way a typed op — which
|
||||
// receives a context and nothing else — can resolve the tenant. Serve installs
|
||||
// one app-wide too; nesting is harmless (the inner one is what the handler sees)
|
||||
// and this package's own tests mount on a bare app with no Serve, so this
|
||||
// install is what makes them pass.
|
||||
// cloud.Bridge is absent on purpose: the composer owns that install — the fused
|
||||
// host at its root, a plugin program in its constructor — and it is what parks
|
||||
// the validated org, the only way a typed op (a context and nothing else) can
|
||||
// resolve the tenant.
|
||||
//
|
||||
// noStore takes the ROOT form, before the leaf because fiber runs middleware in
|
||||
// registration order. A group at /v1/audit cannot carry it: the op below is
|
||||
// declared on the App, so that group would hold middleware with no routes
|
||||
// beneath it, a program zip refuses to compose. Through the scoped Router the
|
||||
// root form is confined to /v1/audit, the one prefix this subsystem declares.
|
||||
//
|
||||
// USE, NOT A MIDDLEWARE-CARRYING GROUP. This used to say
|
||||
// `app.Group("/v1/audit", Bridge(), noStore())`, and that is the one shape this
|
||||
@@ -110,7 +113,7 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
// joining "/v1/audit" with "" yields "/v1/audit/", a different path from the one
|
||||
// this API has always served, and one that would ship in OpenAPI and the SDK.
|
||||
func routes(app cloud.Router, zapp *zip.App, s *cloud.Service[state]) {
|
||||
app.Use(zip.H(cloud.Bridge()), zip.H(noStore()))
|
||||
app.Use(noStore())
|
||||
o := ops{s: s}
|
||||
zip.Get(zapp, "/v1/audit", o.list)
|
||||
}
|
||||
|
||||
@@ -37,9 +37,16 @@ func newStore(t *testing.T) *audit.Recorder {
|
||||
return rec
|
||||
}
|
||||
|
||||
// compose gives the test app what every real composer gives its program: the
|
||||
// fused host installs cloud.Bridge at its root and a plugin program's
|
||||
// constructor does the same, so a bare test app that skipped it would answer
|
||||
// 401 for a reason production can never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func mountApp(t *testing.T, store *audit.Recorder) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), Audit: store}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -186,16 +186,15 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
// SDK method.
|
||||
//
|
||||
// The two subtrees are declared on their OWN groups, because they are two surfaces:
|
||||
// /v1/authors is the tenant's, /v1/admin/authors is the platform's. Both get
|
||||
// cloud.Bridge, which is what parks the validated org a typed op reads — never an In
|
||||
// field, which is caller-supplied and would be a cross-tenant read the caller
|
||||
// asserted for itself. fiber runs middleware in registration order, so each Use
|
||||
// precedes its own leaves; nesting under Serve's own Bridge is harmless.
|
||||
// /v1/authors is the tenant's, /v1/admin/authors is the platform's. A typed op reads
|
||||
// the validated org parked on the context by cloud.Bridge — never an In field, which
|
||||
// is caller-supplied and would be a cross-tenant read the caller asserted for itself.
|
||||
// The composer owns that install, once at its root; both groups here are bare path
|
||||
// prefixes.
|
||||
func routes(app cloud.Router, zapp *zip.App, s *cloud.Service[state]) {
|
||||
o := ops{s: s}
|
||||
|
||||
g := app.Group("/v1/authors")
|
||||
g.Use(cloud.Bridge())
|
||||
// The root of the tenant surface, declared on the App with its whole path rather
|
||||
// than on the group with an empty leaf: joining "/v1/authors" with "" yields
|
||||
// "/v1/authors/", a DIFFERENT path from the one it has always served.
|
||||
@@ -206,7 +205,6 @@ func routes(app cloud.Router, zapp *zip.App, s *cloud.Service[state]) {
|
||||
zip.Post(g, "/deploys/record", o.recordDeploy)
|
||||
|
||||
ga := app.Group("/v1/admin/authors")
|
||||
ga.Use(cloud.Bridge())
|
||||
zip.Get(zapp, "/v1/admin/authors", o.adminList)
|
||||
zip.Post(ga, "/sweep", o.adminSweep)
|
||||
zip.Post(ga, "/:id/approve", o.adminApprove)
|
||||
|
||||
@@ -137,6 +137,13 @@ func (g *fakeGitHub) fetchFile(_ context.Context, _, owner, repo, branch, path s
|
||||
return g.files[owner+"/"+repo+"@"+branch+"/"+path], nil
|
||||
}
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root. In a
|
||||
// test the test IS the composer, so it owes the same thing — a test that skips it
|
||||
// tests a program where every org-scoped op answers 403 for a reason that would
|
||||
// never exist in production. Same helper apps/integrations uses.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mount builds an authors app backed by a fresh store + injected fakes, returning the
|
||||
// app, the service, and the fakes for assertions.
|
||||
func mount(t *testing.T) (*zip.App, *cloud.Service[state], *fakeCommerce, *fakeGitHub) {
|
||||
@@ -159,6 +166,7 @@ func mount(t *testing.T) (*zip.App, *cloud.Service[state], *fakeCommerce, *fakeG
|
||||
},
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, app, s)
|
||||
return app, s, fc, fg
|
||||
}
|
||||
@@ -917,6 +925,7 @@ func sweptAccrued(t *testing.T, body []byte) int {
|
||||
// a temp DataDir, proving the package boots as the binary loads it.
|
||||
func TestMount(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir(), Brand: "hanzo"}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
+4
-5
@@ -118,11 +118,10 @@ func routes(app cloud.Router, s *cloud.Service[struct{}]) {
|
||||
g := app.Group("/v1/auto")
|
||||
o := ops{s: s}
|
||||
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated
|
||||
// principal reaches it by being parked there — never as an In field,
|
||||
// which is caller-supplied and would be a cross-tenant read the caller
|
||||
// asserted for itself.
|
||||
g.Use(cloud.Bridge())
|
||||
// cloud.Bridge is not installed here: the composer installs it once at the
|
||||
// root, after the identity check that mints the validated org and before any
|
||||
// subsystem registers a route — an order only the whole program can assert.
|
||||
// The ops below read what it parks off the context.
|
||||
|
||||
zip.Get(g, "/status", o.status)
|
||||
zip.Get(g, "/pieces", o.pieces)
|
||||
|
||||
@@ -37,6 +37,7 @@ func liveApp(t *testing.T) *zip.App {
|
||||
}
|
||||
t.Setenv("AUTO_UPSTREAM", up)
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("autolive"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("autolive"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -241,6 +241,13 @@ func (f *fakeAuto) handler() http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root, after
|
||||
// the identity check that mints the validated org and before any subsystem
|
||||
// registers a route. In production that composer is serve.go. In a test the test
|
||||
// IS the composer, so it owes the same thing.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// harness mounts the app against a fake upstream and returns both.
|
||||
func harness(t *testing.T) (*zip.App, *fakeAuto) {
|
||||
t.Helper()
|
||||
@@ -250,6 +257,7 @@ func harness(t *testing.T) (*zip.App, *fakeAuto) {
|
||||
t.Setenv("AUTO_UPSTREAM", srv.URL)
|
||||
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("autotest"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("autotest"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
@@ -502,6 +510,7 @@ func TestEngineDown503Relays(t *testing.T) {
|
||||
defer srv.Close()
|
||||
t.Setenv("AUTO_UPSTREAM", srv.URL)
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("autotest"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("autotest"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
@@ -522,6 +531,7 @@ func TestStatusIsAnHonestLens(t *testing.T) {
|
||||
}
|
||||
|
||||
down := zip.New(zip.Config{Logger: luxlog.New("autotest"), DisableStartupMessage: true})
|
||||
compose(down)
|
||||
t.Setenv("AUTO_UPSTREAM", "http://127.0.0.1:1") // nothing listens
|
||||
if err := Mount(down, cloud.Deps{Logger: luxlog.New("autotest"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
|
||||
@@ -204,15 +204,9 @@ type ops struct{ s *cloud.Service[state] }
|
||||
// message and an address that lives only in the URL never arrives.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group("/v1/automations")
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org reaches
|
||||
// it by being parked there — never as an In field, which is caller-supplied and
|
||||
// would be a cross-tenant read the caller asserted for itself. fiber runs
|
||||
// middleware in registration order, so this must precede the leaves below.
|
||||
// Serve installs one for the whole binary; this one is what makes the SUBSYSTEM
|
||||
// self-contained, so a harness that mounts it without Serve (this package's own
|
||||
// tests) resolves the same org the server does. Nesting is harmless — the inner
|
||||
// one is what the handler sees.
|
||||
g.Use(cloud.Bridge())
|
||||
// cloud.Bridge is installed by whoever composes the app — the fused host at
|
||||
// its root — never here: the validated org still reaches every typed op below
|
||||
// because the root install parks it on the context.
|
||||
|
||||
o := ops{s: s}
|
||||
zip.Get(g, "/connectors", o.connectors)
|
||||
|
||||
+7
-8
@@ -113,14 +113,13 @@ func Shutdown() error {
|
||||
|
||||
func routes(app cloud.Router, s *cloud.Service[*state]) {
|
||||
g := app.Group("/v1/books")
|
||||
// Bridge FIRST, then noStore: fiber runs middleware in registration order, so
|
||||
// one installed after its leaves never runs. Bridge parks the VALIDATED org on
|
||||
// the context, which is the only way a typed op — which receives a context and
|
||||
// its decoded In and nothing else — can resolve its tenant; noStore carries the
|
||||
// Cache-Control every books answer has always sent. Both are prefix-scoped, and
|
||||
// nesting under Serve's own app-wide Bridge is harmless (the inner one is what
|
||||
// the handler sees). See typed.go.
|
||||
g.Use(cloud.Bridge(), noStore())
|
||||
// noStore carries the Cache-Control header every books answer has always sent.
|
||||
// It precedes the leaves because fiber runs middleware in registration order.
|
||||
// cloud.Bridge is not installed here: the composer owns it — the fused host
|
||||
// installs it once at its root, and the plugin constructor does the same for a
|
||||
// plugin program — and typed ops read the validated org it parks on the
|
||||
// context. See typed.go.
|
||||
g.Use(noStore())
|
||||
|
||||
// TYPED ops, declared on the group: the prefix is part of each op's path and
|
||||
// therefore of every projection — the document, the MCP tool, the CLI command,
|
||||
|
||||
@@ -37,11 +37,19 @@ import (
|
||||
|
||||
var wireCfg = zip.TestConfig{Timeout: 60 * time.Second, FailOnTimeout: true}
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root, and
|
||||
// in a test the test is the composer, so it owes the same install. A test that
|
||||
// skips it does not test a stricter program — it tests one where every op that
|
||||
// reads the org answers a refusal production can never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mountBooks brings up the real /v1/books surface over a temp DataDir: the real
|
||||
// router, the real middleware, the real stores.
|
||||
func mountBooks(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
deps := cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}
|
||||
if err := Mount(app, deps); err != nil {
|
||||
t.Fatalf("mount: %v", err)
|
||||
|
||||
@@ -195,13 +195,10 @@ type ops struct{ s *cloud.Service[state] }
|
||||
// registration below. A typed op answers ONE marshalled value at ONE declared
|
||||
// success status, and these three do not.
|
||||
func routes(app cloud.Router, s *cloud.Service[state], deps cloud.Deps) {
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org
|
||||
// reaches it by being parked there. fiber runs middleware in registration
|
||||
// order, so one installed after its leaves never runs. Installed through the
|
||||
// scope's Use, once per declared prefix, so it lands on exactly the subtrees
|
||||
// this subsystem declares (/v1/bot/connect, /v1/bot/nodes, /v1/bot/peer/invoke)
|
||||
// rather than on a /v1/bot the scope does not own — which would fail the mount.
|
||||
app.Use(cloud.Bridge())
|
||||
// cloud.Bridge is not installed here. Whoever composes the program installs it
|
||||
// once at the root — after the identity check that mints the validated org and
|
||||
// before any subsystem registers a route (serve.go) — because that order is a
|
||||
// property of the whole program and no subsystem can assert it for itself.
|
||||
|
||||
// One transport for the process, not one per dial: it carries the uptime a
|
||||
// node reads out of the handshake, which is the process's, not the socket's.
|
||||
|
||||
@@ -20,6 +20,15 @@ import (
|
||||
// an i/o timeout, which teaches nothing. The generous bound still fails a hang.
|
||||
var botTestCfg = zip.TestConfig{Timeout: 30 * time.Second, FailOnTimeout: true}
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root, after
|
||||
// the identity check that mints the validated org and before any subsystem
|
||||
// registers a route. In production that composer is serve.go. In a test the test
|
||||
// IS the composer, so it owes the same thing — and a test that skips it does not
|
||||
// test a stricter program, it tests a program where every org-scoped op answers
|
||||
// 403 for a reason that would never exist in production.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mountBot builds the surface over a registry the test controls, through the same
|
||||
// routes() the binary calls — so what a test drives is the code that ships.
|
||||
func mountBot(t *testing.T, reg *Registry) *zip.App {
|
||||
@@ -30,6 +39,7 @@ func mountBot(t *testing.T, reg *Registry) *zip.App {
|
||||
State: state{reg: reg},
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
routes(app, s, deps)
|
||||
return app
|
||||
}
|
||||
|
||||
+3
-6
@@ -178,12 +178,9 @@ type ops struct{ s *cloud.Service[state] }
|
||||
// route, the OpenAPI operation, the MCP tool, the CLI command and every generated
|
||||
// SDK method follow. POST /v1/bots/run stays a raw handler; see run for why.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org reaches
|
||||
// it by being parked there — never as an input field, which is caller-supplied.
|
||||
// fiber runs middleware in registration order, so one installed after its leaves
|
||||
// never runs. Installed through the SUBSYSTEM's own router, which scopes it to
|
||||
// the prefix this app declares (/v1/bots) rather than the whole binary.
|
||||
app.Use(cloud.Bridge())
|
||||
// The composer owns cloud.Bridge: the fused host installs it once at its root
|
||||
// and the plugin constructor does the same for a plugin program, so no
|
||||
// subsystem installs it.
|
||||
|
||||
// UNIFIED PAYWALL (server-side enforcement). To gate this group behind the
|
||||
// caller's plan, prepend the middleware to the group:
|
||||
|
||||
@@ -94,6 +94,13 @@ func (f *fakeRuntime) has(org, id string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge:
|
||||
// the program's composer owns it — serve.go at the root of the fused host, the
|
||||
// plugin constructor for a plugin program. In a test the test is the composer, so
|
||||
// it owes the same install; skipping it drives a program where every org-scoped op
|
||||
// answers 403 for a reason production callers never see.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mountWith builds the surface over an injected runtime, exactly as Mount does over
|
||||
// the real one — routes() is the shared registration path, so what a test drives is
|
||||
// the code that ships.
|
||||
@@ -105,6 +112,7 @@ func mountWith(t *testing.T, rt Runtime) *zip.App {
|
||||
State: state{gateway: gatewayBase(), runtime: rt},
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
routes(app, s)
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ func mountFleet(t *testing.T, rt *stubRuntime) *zip.App {
|
||||
t.Setenv(gatewayURLEnv, "https://bot.example.test")
|
||||
rt.start(t)
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
deps := cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}
|
||||
if err := visor.Mount(app, deps); err != nil { // Wire order: visor first — the shadowing mount
|
||||
t.Fatalf("visor.Mount: %v", err)
|
||||
|
||||
@@ -139,13 +139,13 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
func routes(app cloud.Router, zapp *zip.App, s *cloud.Service[state]) {
|
||||
o := ops{s: s}
|
||||
g := app.Group("/v1/campaign")
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org
|
||||
// reaches it by being parked there — never as an In field, which is
|
||||
// A typed op receives only a context, so the validated org it reads is parked
|
||||
// there by cloud.Bridge — never carried as an In field, which is
|
||||
// caller-supplied and would be a cross-tenant read the caller asserted for
|
||||
// itself. fiber runs middleware in registration order, so this must precede
|
||||
// every leaf below; nesting under Serve's own Bridge is harmless (the inner
|
||||
// one is what the handler sees).
|
||||
g.Use(cloud.Bridge())
|
||||
// itself. This subsystem does not install the bridge: the program's composer
|
||||
// does, once at the root, after the identity check that mints the org and
|
||||
// before any subsystem registers a route — an order only the composer can
|
||||
// hold.
|
||||
|
||||
zip.Get(g, "/summary", o.summary)
|
||||
|
||||
|
||||
@@ -29,9 +29,17 @@ import (
|
||||
|
||||
const wireTimeout = 10 * time.Second
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer does, once at the root. In a test
|
||||
// the test IS the composer, so it owes the same install — skipping it does not
|
||||
// test a stricter program, it tests one where every org-scoped op answers 403
|
||||
// for a reason production could never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func mountWire(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -125,13 +125,6 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
// which of its fields does that.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group("/v1/captable")
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org
|
||||
// reaches it by being parked there — never as an In field, which is
|
||||
// caller-supplied and would be a cross-tenant read the caller asserted for
|
||||
// itself. fiber runs middleware in registration order, so this must precede
|
||||
// the leaves below; it is prefix-scoped, and nesting under Serve's own Bridge
|
||||
// is harmless (the inner one is what the handler sees).
|
||||
g.Use(cloud.Bridge())
|
||||
// Then the bundle's own envelope: a typed op that must answer the bundle's
|
||||
// 400/404/409/500 returns a goja.BundleErr, and this writes those bytes back
|
||||
// verbatim. Also before the leaves, for the same registration-order reason.
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
func mountForFacade(t *testing.T) {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,12 @@ import (
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose installs what the program's composer installs — cloud.Bridge, once at
|
||||
// the app root. A subsystem never installs its own, so a test app owes the same
|
||||
// root install; without it every org-scoped op answers a 403 no production
|
||||
// program would produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mountApp builds a bare zip.App (no SanitizeIdentity middleware, so X-Org-Id +
|
||||
// X-User-Id are trusted verbatim — the standard cloud leaf test harness) and
|
||||
// mounts the captable leaf on it. This exercises the REAL HTTP path: routing →
|
||||
@@ -23,6 +29,7 @@ import (
|
||||
func mountApp(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -433,13 +433,12 @@ func TestScalarCarriesEveryJSONToken(t *testing.T) {
|
||||
// newAppMCP mounts captable the way the SERVER does: one cloud.Bridge at the app
|
||||
// ROOT. zip's own projections of the typed-op registry — the MCP endpoint at /mcp
|
||||
// and the call plane at /.well-known/zip/op/ — are ordinary routes on the app
|
||||
// itself, so they sit OUTSIDE the /v1/captable group and the group's own Bridge
|
||||
// never runs for them. cloud.Listen installs the root one, which is what gives them
|
||||
// a validated org in production.
|
||||
// itself, outside the /v1/captable group, so only the root install reaches them.
|
||||
// cloud.Listen makes that root install in production.
|
||||
func newAppMCP(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
app.Use(cloud.Bridge())
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
+10
-19
@@ -219,27 +219,18 @@ func build(b cloud.Base) (state, error) {
|
||||
|
||||
// routes registers the lens.
|
||||
//
|
||||
// Bridge FIRST and BEFORE the leaf: fiber runs middleware in registration order,
|
||||
// so one installed after its route never runs. A typed op receives only a
|
||||
// context, so the validated org reaches it by being parked there — never as an
|
||||
// In field, which is caller-supplied and would be a cross-tenant read the caller
|
||||
// asserted for itself. Serve installs one app-wide too; nesting is harmless, and
|
||||
// this package's own tests mount on a bare app with no Serve, so this install is
|
||||
// what makes them pass.
|
||||
// A typed op receives only a context, so the validated org reaches it by being
|
||||
// parked there — never as an In field, which is caller-supplied and would be a
|
||||
// cross-tenant read the caller asserted for itself. cloud.Bridge parks it, and
|
||||
// the COMPOSER installs it, not this subsystem: the fused host once at its root
|
||||
// (serve.go), and a plugin program's constructor likewise. The install this
|
||||
// subsystem used to make sat on a group with no routes beneath it, a program zip
|
||||
// refuses to compose.
|
||||
//
|
||||
// USE, NOT A MIDDLEWARE-CARRYING GROUP — the same correction as auditlog, for
|
||||
// the same reason. This used to say `app.Group("/v1/catalog", Bridge())`, but
|
||||
// the op below is declared on the App with its WHOLE path, so no route sits
|
||||
// beneath that group; middleware over an empty subtree can never run, and zip
|
||||
// refuses to compose it. Use is the ONE composition verb, and it means the right
|
||||
// thing through both routers: cloud's scope gates it to this subsystem's
|
||||
// declared subtrees, and a bare *zip.App treats root middleware as always-live.
|
||||
//
|
||||
// The op keeps its WHOLE path rather than moving to a group with an empty leaf:
|
||||
// joining "/v1/catalog" with "" yields "/v1/catalog/", a different path from the
|
||||
// one this API has always served, and one that would ship in OpenAPI and the SDK.
|
||||
// The op is declared on the App with its WHOLE path, not on a group with an
|
||||
// empty leaf: joining "/v1/catalog" with "" yields "/v1/catalog/", a different
|
||||
// path from the one this API has always served.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
app.Use(zip.H(cloud.Bridge()))
|
||||
o := ops{s: s}
|
||||
zip.Get(cloud.ZipApp(app), "/v1/catalog", o.browse)
|
||||
}
|
||||
|
||||
@@ -15,11 +15,18 @@ import (
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge
|
||||
// (routes says why): the program's composer owns it — serve.go in production — so
|
||||
// a test app owes the same install, else every org-scoped op answers 403 for a
|
||||
// reason no composed program has.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mount brings up the index (the store) and the catalog (the lens) on one app,
|
||||
// exactly as apps.go orders them.
|
||||
func mount(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{DisableStartupMessage: true})
|
||||
compose(app)
|
||||
deps := cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}
|
||||
if err := index.Mount(app, deps); err != nil {
|
||||
t.Fatalf("index.Mount: %v", err)
|
||||
|
||||
@@ -54,6 +54,12 @@ type testEnv struct {
|
||||
dataDir string
|
||||
}
|
||||
|
||||
// compose gives the test app what every real composer gives its program: the
|
||||
// fused host installs cloud.Bridge at its root and a plugin program's
|
||||
// constructor does the same, so a bare test app that skipped it would answer
|
||||
// 403 for a reason production can never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// newApp mounts channels exactly as apps.go does. Integrations stays
|
||||
// unmounted on purpose: LinkedSubject fails soft (empty UserID),
|
||||
// OrgForExternalID / ConnectionFor answer not-found — the fail-closed side
|
||||
@@ -62,6 +68,7 @@ func newApp(t *testing.T) *testEnv {
|
||||
t.Helper()
|
||||
logs := &syncBuf{}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
dataDir := t.TempDir()
|
||||
deps := cloud.Deps{Logger: luxlog.NewWriter(logs), DataDir: dataDir, Domain: "api.hanzo.ai"}
|
||||
if err := Mount(app, deps); err != nil {
|
||||
|
||||
@@ -52,14 +52,10 @@ func routes(app cloud.Router, s *cloud.Service[state]) error {
|
||||
if zapp == nil {
|
||||
return fmt.Errorf("channels.Mount: router exposes no zip.App, so no typed op could be registered")
|
||||
}
|
||||
// A typed op receives only a context, so the validated org has to be parked
|
||||
// there. cloud.Bridge parks it, and the composer owns that install: the
|
||||
// fused host at its root, a plugin program in its constructor.
|
||||
g := app.Group("/v1/channels")
|
||||
// The Bridge FIRST, bounded to the subtree channels owns: a typed op receives
|
||||
// only a context, so the validated org has to be parked there, and fiber runs
|
||||
// middleware in registration order — one installed after these leaves would
|
||||
// never run. cloud.Listen installs one app-wide too; nesting is harmless, and
|
||||
// having it here is what makes this package's own tests — which mount on a
|
||||
// bare app — exercise the same tenancy the binary does.
|
||||
g.Use(cloud.Bridge())
|
||||
|
||||
o := ops{s: s}
|
||||
// cloud.Terminal is func(func(*zip.Ctx) error) func(*zip.Ctx) error; zip.Middleware
|
||||
|
||||
@@ -331,6 +331,7 @@ func meteredHarness(t *testing.T, tokens map[string]string, rec *capture, result
|
||||
t.Cleanup(func() { tokenFor = prev })
|
||||
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir(), Metering: m}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -171,13 +171,11 @@ func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group("/v1/cloudflare")
|
||||
o := ops{s: s}
|
||||
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org
|
||||
// reaches it by being parked there — never as an In field, which is
|
||||
// caller-supplied and would be a cross-tenant read the caller asserted for
|
||||
// itself. fiber runs middleware in registration order, so this must precede
|
||||
// the leaves below; it is prefix-scoped, and nesting under Serve's own Bridge
|
||||
// is harmless (the inner one is what the handler sees).
|
||||
g.Use(cloud.Bridge())
|
||||
// A typed op receives only a context, so the validated org reaches it by
|
||||
// being parked there — never as an In field, which is caller-supplied and
|
||||
// would be a cross-tenant read the caller asserted for itself. cloud.Bridge
|
||||
// does the parking, and the composer owns that install, once at its root;
|
||||
// this group is a bare path prefix.
|
||||
|
||||
// Zones + Analytics (read) — enumerate the org's zones and read a zone's traffic
|
||||
// analytics; the zone ids feed Workers routes and analytics. Zone/record
|
||||
|
||||
@@ -87,6 +87,13 @@ func fakeCF(rec *capture, resultFor func(path string) (int, string)) http.Handle
|
||||
}
|
||||
}
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root. In a
|
||||
// test the test IS the composer, so it owes the same thing — a test that skips it
|
||||
// tests a program where every org-scoped op answers 403 for a reason that would
|
||||
// never exist in production. Same helper apps/integrations uses.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// harness mounts the subsystem against a fake CF and per-org token seam.
|
||||
func harness(t *testing.T, tokens map[string]string, rec *capture, resultFor func(string) (int, string)) *zip.App {
|
||||
t.Helper()
|
||||
@@ -108,6 +115,7 @@ func harness(t *testing.T, tokens map[string]string, rec *capture, resultFor fun
|
||||
t.Cleanup(func() { tokenFor = prev })
|
||||
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
+6
-10
@@ -105,18 +105,14 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
}
|
||||
|
||||
// routes registers the /v1/code surface. It is a FUNCTION rather than inline in
|
||||
// Mount so this package's own tests drive the REAL registration — the Bridge
|
||||
// included — instead of a reconstruction of it that can drift from what the
|
||||
// binary serves.
|
||||
// Mount so this package's own tests drive the REAL registration instead of a
|
||||
// reconstruction of it that can drift from what the binary serves.
|
||||
func routes(app cloud.Router, s *service) error {
|
||||
g := app.Group("/v1/code")
|
||||
// The Bridge FIRST, bounded to the subtree code owns: a typed op receives only
|
||||
// a context, so the validated org has to be parked there, and fiber runs
|
||||
// middleware in registration order — one installed after these leaves would
|
||||
// never run. cloud.Listen installs one app-wide too; nesting is harmless, and
|
||||
// having it here is what makes this package's own tests — which mount on a
|
||||
// bare app — exercise the same tenancy the binary does.
|
||||
g.Use(cloud.Bridge())
|
||||
// cloud.Bridge is not installed here: the composer installs it once at the
|
||||
// root, after the identity check that mints the validated org and before any
|
||||
// subsystem registers a route — an order only the whole program can assert.
|
||||
// The ops below read what it parks off the context.
|
||||
|
||||
// Every route is a TYPED op: one registry entry, which is what the OpenAPI
|
||||
// operation, the MCP tool, the CLI command and every generated SDK method are
|
||||
|
||||
@@ -69,14 +69,21 @@ func newTestService(t *testing.T) *service {
|
||||
return s
|
||||
}
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root, after
|
||||
// the identity check that mints the validated org and before any subsystem
|
||||
// registers a route. In production that composer is serve.go. In a test the test
|
||||
// IS the composer, so it owes the same thing.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func newTestApp(t *testing.T) (*zip.App, *service) {
|
||||
t.Helper()
|
||||
s := newTestService(t)
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
// The REAL registration, not a reconstruction of it: routes() is what Mount
|
||||
// calls, so the Bridge that carries the validated org to every typed op is
|
||||
// exercised here exactly as the binary installs it. A hand-listed copy drifts
|
||||
// silently the first time a route moves.
|
||||
// calls, so every typed op is exercised here exactly as the binary serves it.
|
||||
// A hand-listed copy drifts silently the first time a route moves.
|
||||
if err := routes(app, s); err != nil {
|
||||
t.Fatalf("routes: %v", err)
|
||||
}
|
||||
|
||||
@@ -219,8 +219,9 @@ func TestSearchDegradedKeyIsConditional(t *testing.T) {
|
||||
|
||||
// TestFailsClosedWithoutAValidatedPrincipal is the tenancy claim: no request
|
||||
// field can name the tenant, so an anonymous caller reads and writes nothing —
|
||||
// and the Bridge routes() installs is what carries the validated org to every op,
|
||||
// so this also proves that middleware precedes the leaves.
|
||||
// and compose has installed cloud.Bridge at the root exactly as the program's
|
||||
// composer does, so each 403 is an op refusing a missing org, never an artifact
|
||||
// of a missing install.
|
||||
func TestFailsClosedWithoutAValidatedPrincipal(t *testing.T) {
|
||||
app, _ := newTestApp(t)
|
||||
for _, r := range []struct {
|
||||
|
||||
@@ -185,14 +185,9 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
func routes(app cloud.Router, zapp *zip.App, s *cloud.Service[state]) {
|
||||
o := ops{s: s}
|
||||
g := app.Group("/v1/company")
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org
|
||||
// reaches it by being parked there — never as an In field, which is
|
||||
// caller-supplied and would be a cross-tenant read the caller asserted for
|
||||
// itself. fiber runs middleware in registration order, so this must precede
|
||||
// every leaf below; it is prefix-scoped, and nesting under Serve's own Bridge
|
||||
// is harmless (the inner one is what the handler sees). It also carries the
|
||||
// 201 an op cannot state in its own signature — see begin.
|
||||
g.Use(cloud.Bridge())
|
||||
// cloud.Bridge is installed by whoever composes the app — the fused host at
|
||||
// its root — never here. The root install is also what carries begin's 201
|
||||
// back out (cloud.Created writes into the slot that install parked).
|
||||
|
||||
// UNTYPED, and registered HERE so the body cap below never applies to it: the
|
||||
// deck is document BYTES, not JSON, and its size ceiling has always been the
|
||||
|
||||
@@ -146,12 +146,11 @@ func init() {
|
||||
// split its two 200 shapes (reconciled check vs benign unknown-reference no-op).
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group(routePrefix)
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org
|
||||
// reaches it by being parked there — never as an In field, which is
|
||||
// caller-supplied and would be a cross-tenant read the caller asserted for
|
||||
// itself. fiber runs middleware in registration order, so this precedes the
|
||||
// leaves below.
|
||||
g.Use(cloud.Bridge())
|
||||
// cloud.Bridge is not installed here: the composer owns it — the fused host
|
||||
// installs it once at its root, and the plugin constructor does the same for a
|
||||
// plugin program — and typed ops read the validated org it parks on the
|
||||
// context. bodyCap precedes the leaves because fiber runs middleware in
|
||||
// registration order.
|
||||
g.Use(bodyCap())
|
||||
o := ops{s: s}
|
||||
|
||||
|
||||
@@ -45,6 +45,13 @@ func (f fakeProvider) Check(context.Context, string, string) (idv.Result, error)
|
||||
return idv.Result{Ref: "ref_" + f.name, Status: f.checkStatus}, nil
|
||||
}
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root, and
|
||||
// in a test the test is the composer, so it owes the same install. A test that
|
||||
// skips it does not test a stricter program — it tests one where every op that
|
||||
// reads the org answers a refusal production can never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mount brings compliance up on a bare app with a real audit recorder, and
|
||||
// returns the app + the recorder for assertions. The provider defaults to Manual.
|
||||
func mount(t *testing.T) (*zip.App, *audit.Recorder) {
|
||||
@@ -55,6 +62,7 @@ func mount(t *testing.T) (*zip.App, *audit.Recorder) {
|
||||
t.Fatalf("audit.Open: %v", err)
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir(), Audit: rec}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
@@ -173,6 +181,7 @@ func mountWithWebhook(t *testing.T, secret string) *zip.App {
|
||||
t.Fatalf("audit.Open: %v", err)
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
deps := cloud.Deps{
|
||||
Logger: luxlog.New("test"), DataDir: t.TempDir(), Audit: rec,
|
||||
KMS: fakeKMS{ref: "kms://idv-webhook", secret: []byte(secret)},
|
||||
|
||||
@@ -27,7 +27,7 @@ func doRaw(t *testing.T, app *zip.App, rq *http.Request) *http.Response {
|
||||
}
|
||||
|
||||
// TestHealthNeedsNoPrincipal pins the ONE route on this surface that does not
|
||||
// read a tenant. It is the route the group's cloud.Bridge could most easily have
|
||||
// read a tenant. It is the route the composer's cloud.Bridge could most easily have
|
||||
// broken: Bridge is what parks the validated org for every other op, and if it
|
||||
// REFUSED a request that carries no org, installing it in front of the leaves
|
||||
// would have turned liveness into a 403 — the failure mode where a subsystem
|
||||
|
||||
@@ -36,13 +36,12 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
}
|
||||
log := deps.Logger
|
||||
g := app.Group("/v1/automations/connectors")
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org
|
||||
// reaches it by being parked there — never as an In field, which is
|
||||
// caller-supplied. When this package is mounted through automations.Mount the
|
||||
// automations group's Bridge already covers the path; this one is what makes
|
||||
// the SUBSYSTEM self-contained when mounted alone. Nesting is harmless — the
|
||||
// inner one is what the handler sees.
|
||||
g.Use(cloud.Bridge())
|
||||
// cloud.Bridge is not installed here. Whoever composes the program installs it
|
||||
// once at the root — after the identity check that mints the validated org and
|
||||
// before any subsystem registers a route (serve.go) — because that order is a
|
||||
// property of the whole program and no subsystem can assert it for itself. The
|
||||
// validated org still reaches the typed op only off the context, never as an
|
||||
// In field, which is caller-supplied.
|
||||
zip.Post(g, "/:id/run", run)
|
||||
if log != nil {
|
||||
log.New("subsystem", "connectorruntime").Info(
|
||||
|
||||
@@ -14,12 +14,21 @@ import (
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (Mount says why): the program's composer installs it once at the root, after
|
||||
// the identity check that mints the validated org and before any subsystem
|
||||
// registers a route. In production that composer is serve.go. In a test the test
|
||||
// IS the composer, so it owes the same thing — and a test that skips it does not
|
||||
// test a stricter program, it tests a program where every org-scoped op answers
|
||||
// 403 for a reason that would never exist in production.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// newApp mounts connectorruntime ALONE — no automations group around it — which is
|
||||
// what proves the subsystem self-contained: its own Bridge is the only thing that
|
||||
// parks the org the typed op resolves.
|
||||
// what proves the subsystem composes on its own.
|
||||
func newApp(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test")}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -100,17 +100,11 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
// channels/generate/publish) never collide with the parameterised transition route
|
||||
// (which is three segments deep), so registration order is not load-bearing here.
|
||||
//
|
||||
// cloud.Bridge is installed on the group FIRST, ahead of every leaf: a typed op
|
||||
// receives a context.Context and its decoded In and nothing else, so the validated
|
||||
// org crosses on the context and there is no request for it to read. Serve installs
|
||||
// the same middleware for the whole binary, and nesting is harmless (the inner one
|
||||
// is the one the handler sees) — it is declared here so the subsystem is
|
||||
// self-sufficient: mounted on a bare app, by a test or by any composition root that
|
||||
// is not Serve, its typed ops still resolve their tenant instead of refusing every
|
||||
// caller.
|
||||
// The composer owns cloud.Bridge: the fused host installs it once at its root
|
||||
// and the plugin constructor does the same for a plugin program, so no
|
||||
// subsystem installs it.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group("/v1/content")
|
||||
g.Use(cloud.Bridge())
|
||||
o := contentOps{s: s}
|
||||
zip.Get(g, "/lifecycle", o.getLifecycle)
|
||||
zip.Get(g, "/board", o.getBoard)
|
||||
|
||||
@@ -111,9 +111,17 @@ func TestEnforceLifecycleHook(t *testing.T) {
|
||||
|
||||
// ---- integration: the loop end-to-end over the REAL framework store ----
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge:
|
||||
// the program's composer owns it — serve.go at the root of the fused host, the
|
||||
// plugin constructor for a plugin program. In a test the test is the composer, so
|
||||
// it owes the same install; skipping it drives a program where every org-scoped op
|
||||
// answers 403 for a reason production callers never see.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func mountContent(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := framework.Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("framework.Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ func mountWith(t *testing.T, deps cloud.Deps) *zip.App {
|
||||
deps.Domain = "api.test"
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := framework.Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("framework.Mount: %v", err)
|
||||
}
|
||||
|
||||
+5
-7
@@ -165,13 +165,11 @@ type ops struct{ s *cloud.Service[state] }
|
||||
// roll-up, and the Startup Program application intake.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group("/v1/crm")
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org
|
||||
// reaches it by being parked there. fiber runs middleware in registration
|
||||
// order, so one installed after its leaves never runs. Serve installs the same
|
||||
// bridge for the whole binary; nesting is harmless (the inner one is what the
|
||||
// handler sees) and this keeps the subsystem's ops org-scoped wherever it is
|
||||
// mounted — including a test app that never calls Serve.
|
||||
g.Use(cloud.Bridge())
|
||||
// A typed op receives only a context, so the validated org it reads is parked
|
||||
// there by cloud.Bridge. This subsystem does not install it: the program's
|
||||
// composer does, once at the root, after the identity check that mints the org
|
||||
// and before any subsystem registers a route — an order only the composer can
|
||||
// hold.
|
||||
|
||||
// Declared on the GROUP: the op's path is the prefix composed with the leaf,
|
||||
// which is the identity every projection keys on, and cmd/zipdoc (zip v1.18.3+)
|
||||
|
||||
@@ -13,9 +13,17 @@ import (
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer does, once at the root. In a test
|
||||
// the test IS the composer, so it owes the same install — skipping it does not
|
||||
// test a stricter program, it tests one where every org-scoped op answers 403
|
||||
// for a reason production could never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func mountApp(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ const product = "risk"
|
||||
func published(t *testing.T) *openapi.Document {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("datasettest"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := mount(newPlane(&fake{}), app); err != nil {
|
||||
t.Fatalf("mount: %v", err)
|
||||
}
|
||||
|
||||
+11
-17
@@ -147,16 +147,16 @@ type scan struct {
|
||||
since time.Time
|
||||
}
|
||||
|
||||
// Mount wires the dataset leaves of /v1/risk onto app.
|
||||
// Mount registers the dataset leaves of /v1/risk onto app.
|
||||
//
|
||||
// EVERY INHERITED CAPABILITY IS WIRED HERE, EXPLICITLY. Being embedded in cloud
|
||||
// EVERY INHERITED CAPABILITY IS NAMED HERE, EXPLICITLY. Being embedded in cloud
|
||||
// makes each one AVAILABLE; none is automatic:
|
||||
//
|
||||
// IAM auth SanitizeIdentity mints X-Org-Id from the verified bearer, in
|
||||
// serve.go. This app never validates a token and never can.
|
||||
// tenant gate cloud.Bridge() on the group, FIRST, before any leaf — a typed op
|
||||
// receives only a context, and Bridge is what parks the validated
|
||||
// org in it. fiber orders middleware by registration.
|
||||
// tenant gate cloud.Bridge parks the validated org on the context a typed op
|
||||
// receives; it is the composer's install — once at the root of
|
||||
// every program — so this package does not install it.
|
||||
// meter+gate cloud.NewResourceMeter(deps, "dataset"); Gate before the one
|
||||
// priced op and Meter after it.
|
||||
// logs cloud.NewBase(deps, "dataset") gives the scoped logger.
|
||||
@@ -226,28 +226,22 @@ func (p *plane) ready(ctx context.Context) error {
|
||||
// plane came from — the same split apps/ml makes, for the same reason: a test
|
||||
// pins the plane and exercises the wire.
|
||||
func mount(p *plane, app cloud.Router) error {
|
||||
// TWO SEAMS, EACH FOR WHAT IT IS FOR.
|
||||
//
|
||||
// app.Use installs the tenant bridge ONCE PER DECLARED PREFIX — the scope
|
||||
// reads manifest.Apps for that — so the middleware lands exactly on
|
||||
// /v1/risk/datasets and nowhere else. Grouping at /v1/risk to get a shorter leaf
|
||||
// would install it across a subtree this app does not own — that stem is the
|
||||
// decision plane's — which is the escape the scope exists to refuse.
|
||||
//
|
||||
// cloud.ZipApp recovers the typed-op registry, which the Router interface does
|
||||
// not carry, and the ops register at ABSOLUTE paths on it. That is what keeps
|
||||
// the published address exactly `/v1/risk/datasets` — a group root composes to
|
||||
// `/v1/risk/datasets/`, and a trailing slash in the document is a trailing slash
|
||||
// in every generated SDK.
|
||||
//
|
||||
// Bridge FIRST, before any leaf: a typed op receives only a context, and this
|
||||
// is what parks the validated org that tenant.Of reads. fiber runs middleware
|
||||
// in registration order, so an install after its leaves never runs.
|
||||
// cloud.Bridge is the composer's install — once at the root of every program —
|
||||
// so this package installs only its own envelope. app.Use lands that ONCE PER
|
||||
// DECLARED PREFIX — the scope reads manifest.Apps for that — so it covers
|
||||
// /v1/risk/datasets and nowhere else; grouping at /v1/risk for a shorter leaf
|
||||
// would spread it across a subtree this app does not own — that stem is the
|
||||
// decision plane's — which is the escape the scope exists to refuse.
|
||||
z := cloud.ZipApp(app)
|
||||
if z == nil {
|
||||
return fmt.Errorf("dataset.Mount: the router carries no typed-op registry")
|
||||
}
|
||||
app.Use(cloud.Bridge())
|
||||
// DenyEnvelope renders a gate refusal as the money wire's own bytes, the same
|
||||
// ones every other Hanzo surface emits — one error contract for "no funds"
|
||||
// across the fleet instead of this plane's private spelling of it. It touches
|
||||
|
||||
@@ -83,9 +83,16 @@ func (l *ledger) charged() []int64 {
|
||||
return append([]int64(nil), l.debit...)
|
||||
}
|
||||
|
||||
// compose installs what the program's composer installs — cloud.Bridge, once at
|
||||
// the app root. A subsystem never installs its own, so a test app owes the same
|
||||
// root install; without it every org-scoped op answers a 403 no production
|
||||
// program would produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func mountHTTP(t *testing.T, p *plane) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
if err := mount(p, app); err != nil {
|
||||
t.Fatalf("mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
func projections(t *testing.T) (served map[string]bool, typed map[string]*openapi.Operation) {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("datasettest"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := mount(newPlane(&fake{}), app); err != nil {
|
||||
t.Fatalf("mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
func listApplicationsAs(t *testing.T, s *cloud.Service[state], headers map[string]string) (int, map[string]any) {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, s)
|
||||
req := httptest.NewRequest("GET", "/v1/deploy/applications", nil)
|
||||
for k, v := range headers {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"github.com/hanzoai/cloud"
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// compose stands in for the composer. Production programs are built by
|
||||
// cloud.App, which installs the principal enrichment once at the root before
|
||||
// any route; a test that mounts this subsystem on a bare app owns that duty
|
||||
// itself, exactly once, here. A test that sends no identity is unaffected —
|
||||
// with nothing validated there is nothing to park — so anonymous cases still
|
||||
// refuse, and principal-carrying cases reach the handler as they do in
|
||||
// production.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
@@ -48,7 +48,7 @@ const dashPrefix = "/v1/deploy"
|
||||
|
||||
// registerDashboardRoutes wires the ArgoCD-UI-compatible API surface (no FE —
|
||||
// the SPA is a separate hanzoai/spa App). Called from routes() (deploy.go), which
|
||||
// owns the group and installs the bridge and the sign-in bounce on it.
|
||||
// installs the sign-in bounce so it covers this prefix.
|
||||
//
|
||||
// Every READ here is a TYPED op declared on that group, so the op's path is the
|
||||
// prefix composed with the leaf — the identity every projection keys on — and the
|
||||
@@ -62,7 +62,11 @@ func registerDashboardRoutes(app cloud.Router, s *cloud.Service[state]) {
|
||||
// `g := <router>.Group("/prefix")` in the SAME file, and a prefix it cannot
|
||||
// resolve is prose filed under the wrong path and silently dropped from both the
|
||||
// document and the MCP tool — so it refuses, naming the file, the line and this
|
||||
// fix. routes() (deploy.go) hangs the group's middleware off the same prefix.
|
||||
// fix. The group is a bare path prefix carrying NO middleware, which is what
|
||||
// keeps it composable: the leaves below register through THIS node, and
|
||||
// middleware belongs on the root gated by path (routes(), deploy.go), because a
|
||||
// second Group at the same prefix would be a fresh node — middleware there
|
||||
// stands beside these routes, not above them, and zip judges the node.
|
||||
g := app.Group(dashPrefix)
|
||||
o := ops{s: s}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
func getJSON(t *testing.T, s *cloud.Service[state], path string) map[string]any {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, s)
|
||||
req := httptest.NewRequest("GET", path, nil)
|
||||
req.Header.Set("X-User-IsAdmin", "true")
|
||||
@@ -136,6 +137,7 @@ func TestDashProjectsEndpoint_PrefersRealCRs(t *testing.T) {
|
||||
// the SuperAdmin claim (fail-closed, no fleet/cluster data to an anonymous caller).
|
||||
func TestNewRoutesRequireAdmin(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, fakeService())
|
||||
for _, path := range []string{"/v1/deploy/clusters", "/v1/deploy/projects", "/v1/deploy/stream/applications"} {
|
||||
// EventSource sends Accept: text/event-stream + Sec-Fetch-Dest: empty, so a
|
||||
@@ -162,6 +164,7 @@ func TestNewRoutesRequireAdmin(t *testing.T) {
|
||||
// the writer eagerly on SendStreamWriter — see setStreamHeaders).
|
||||
func TestStreamSetsSSEHeaders(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
c := app.TestCtx("GET", "/v1/deploy/stream/applications")
|
||||
setStreamHeaders(c)
|
||||
resp := c.Fiber().Response()
|
||||
@@ -184,6 +187,7 @@ func TestStreamSetsSSEHeaders(t *testing.T) {
|
||||
func TestStreamFailsClosedWithoutCluster(t *testing.T) {
|
||||
noK8s := &cloud.Service[state]{Base: cloud.Base{Log: luxlog.New("test")}, State: state{initErr: "no kubeconfig"}}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
c := app.TestCtx("GET", "/v1/deploy/stream/applications")
|
||||
err := dashStreamApps(noK8s, c)
|
||||
if err == nil {
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
// /v1/deploy/ui route without wrapping it in guard() breaks this test.
|
||||
func TestDeployRoutesRequireAdmin(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, fakeService()) // the COMPLETE surface: native + engine + dashboard
|
||||
|
||||
guarded := []struct{ method, path string }{
|
||||
@@ -109,6 +110,7 @@ func TestDeployRoutesRequireAdmin(t *testing.T) {
|
||||
// anonymous answer carries the sign-in URL and NOTHING that identifies anyone.
|
||||
func TestUserInfoIsPublicBootstrap(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, fakeService())
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest("GET", "/v1/deploy/session/userinfo", nil))
|
||||
|
||||
+20
-25
@@ -296,35 +296,30 @@ func init() {
|
||||
// routes registers the /v1/deploy/* surface. Every observing/mutating route is
|
||||
// SuperAdmin-gated; the health probe is public (real k8s reachability).
|
||||
//
|
||||
// The first statement is the plane's MIDDLEWARE, hung on the /v1/deploy prefix so
|
||||
// it reaches every route below — the typed ops in dashboard.go included, which
|
||||
// register their own leaves on the same prefix. It carries the two facts every
|
||||
// route beneath it needs:
|
||||
//
|
||||
// - cloud.Bridge, which parks the request on the context so a TYPED op — which
|
||||
// receives only a context — can still reach the validated principal its
|
||||
// scope is derived from (typed.go). Serve installs the same bridge for the
|
||||
// whole binary; nesting is harmless (the inner one is what the handler sees)
|
||||
// and this keeps the ops scoped wherever they are mounted, including a test
|
||||
// app that never calls Serve.
|
||||
// - bounce, which turns a REFUSED browser navigation into the sign-in redirect
|
||||
// (scope.go). It is one rule for the typed ops and the raw handlers alike.
|
||||
// The first statement is bounce, which turns a REFUSED browser navigation into the
|
||||
// sign-in redirect (scope.go) — one rule for the typed ops in dashboard.go and the
|
||||
// raw handlers here alike. It is deploy's own presentation rule and nothing else:
|
||||
// the request every typed op reads its principal off is parked on the context by
|
||||
// whoever composes the app, so this surface installs no bridge of its own.
|
||||
//
|
||||
// It is installed FIRST: fiber runs middleware in registration order, so one
|
||||
// installed after its leaves never runs.
|
||||
//
|
||||
// It goes on the ROOT rather than on a Group(dashPrefix) node, and cloud.Router
|
||||
// confines it by PATH — scope.Use gates every request on the prefixes deploy
|
||||
// declares (scope.go, manifest/apps.go). The group form put the middleware on a
|
||||
// node of its own: each Group call makes a fresh node, so it stood beside — not
|
||||
// above — the node dashboard.go creates for its leaves, and zip judges the node,
|
||||
// not the path, so it refuses to compose middleware no route beneath it can reach.
|
||||
// Deploy declares its subtree as an explicit list of /v1/deploy/<resource> paths
|
||||
// rather than /v1/deploy itself, and that list covers every route registered here:
|
||||
// health, login, callback, logout, reconcile, and the dashboard's settings,
|
||||
// session/userinfo, version, account/can-i, applications, stream/applications,
|
||||
// clusters, projects and gitops. What the change drops is /v1/deploy paths with no
|
||||
// route, which answer 404 — and bounce reshapes a 403 and nothing else, so their
|
||||
// answer is what it was.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
// ON THE ROUTER, not on a Group(dashPrefix) of its own. This read
|
||||
// `app.Group(dashPrefix).Use(...)`, and the routes it meant to wrap are
|
||||
// composed under a DIFFERENT app.Group(dashPrefix) — the one dashboard.go
|
||||
// builds. Group returns a NEW definition on every call, so those are two nodes
|
||||
// at the same path: the middleware sat on the empty one, and NEITHER
|
||||
// cloud.Bridge NOR bounce ever ran for a single route of this surface. Every
|
||||
// prefix this subsystem declares is under dashPrefix (manifest/apps.go), so a
|
||||
// scope bounds this to exactly what the group named, and the plugin binary
|
||||
// serves nothing else — the bound is unchanged, it is now a predicate over the
|
||||
// request rather than a place in the tree that the routes turned out not to be
|
||||
// in.
|
||||
app.Use(cloud.Bridge(), zip.H(bounce))
|
||||
app.Use(zip.H(bounce))
|
||||
|
||||
// Liveness — public (probe-able without a JWT). It stays a RAW handler because
|
||||
// it answers 503 carrying the SAME domain body as its 200 (status + the k8s and
|
||||
|
||||
@@ -110,6 +110,7 @@ func TestGitOpsHistoryCap(t *testing.T) {
|
||||
func TestGitOpsGuarded(t *testing.T) {
|
||||
s := fakeService()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, s)
|
||||
resp, err := app.Test(httptest.NewRequest("GET", "/v1/deploy/gitops", nil))
|
||||
if err != nil {
|
||||
|
||||
@@ -99,6 +99,7 @@ func TestWantsDocument(t *testing.T) {
|
||||
// navigation gets bounced to sign-in. Neither is ever served the data.
|
||||
func TestGuardRefusesNonAdmin(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, fakeService())
|
||||
|
||||
// API call (no Accept, the shape every API client and the existing e2e sends).
|
||||
@@ -364,6 +365,7 @@ func TestSignInFailsClosedWithoutPublicOrigin(t *testing.T) {
|
||||
verify: func(string) (cloud.VerifiedIdentity, error) { return cloud.VerifiedIdentity{}, nil },
|
||||
} // publicURL deliberately empty
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, svc)
|
||||
|
||||
// A forged Host must NOT be adopted as the origin.
|
||||
@@ -613,6 +615,7 @@ func signinApp(t *testing.T, issuer string) (*zip.App, *fakeIAM) {
|
||||
},
|
||||
}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, svc)
|
||||
return app, iam
|
||||
}
|
||||
|
||||
@@ -121,8 +121,9 @@ func navigating(c *zip.Ctx) bool {
|
||||
}
|
||||
|
||||
// bounce turns a REFUSED browser navigation into the sign-in redirect, and leaves
|
||||
// every other answer alone. Installed once, on the /v1/deploy group (routes,
|
||||
// deploy.go), so it covers the typed ops and the raw handlers with one rule.
|
||||
// every other answer alone. Installed once, on the root and confined by path to
|
||||
// the /v1/deploy prefixes this subsystem declares (routes, deploy.go), so it
|
||||
// covers the typed ops and the raw handlers with one rule.
|
||||
//
|
||||
// It reads the ANSWER, not the caller: a 403 is the only thing it reshapes, and
|
||||
// only for a request that positively identifies as a document GET — so the API
|
||||
|
||||
@@ -48,6 +48,7 @@ func orgAppCR(ns, name, org, project string) *unstructured.Unstructured {
|
||||
func getAs(t *testing.T, s *cloud.Service[state], path string, headers map[string]string) *http.Response {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, s)
|
||||
req := httptest.NewRequest("GET", path, nil)
|
||||
for k, v := range headers {
|
||||
@@ -97,6 +98,7 @@ func appNames(body map[string]any) map[string]bool {
|
||||
func probeScope(t *testing.T, headers map[string]string) (scope, bool) {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
var got scope
|
||||
var ok bool
|
||||
app.Get("/probe", func(c *zip.Ctx) error {
|
||||
|
||||
@@ -25,6 +25,7 @@ func fakeGit(t *testing.T, rev string, files []plane.File, fault error) {
|
||||
t.Setenv("ZIP_RUNTIME_DIR", t.TempDir())
|
||||
|
||||
app := zip.New(zip.Config{AppName: "git", Logger: luxlog.New("gittest")})
|
||||
compose(app)
|
||||
zip.Post[plane.FilesIn, plane.Files](app, "/git/files",
|
||||
func(ctx context.Context, _ *plane.FilesIn) (*plane.Files, error) {
|
||||
if fault != nil {
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
func probe(t *testing.T, s *cloud.Service[state], req *http.Request) (int, string) {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, s)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
@@ -132,6 +133,7 @@ func TestRefusalIsA403AndANavigationIsBounced(t *testing.T) {
|
||||
nav := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
nav.Header.Set("Sec-Fetch-Dest", "document")
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, s)
|
||||
resp, err := app.Test(nav)
|
||||
if err != nil {
|
||||
@@ -246,6 +248,7 @@ func TestPostsStayRawBecauseZipDecodesTheBodyFirst(t *testing.T) {
|
||||
// carries prose. Nothing on this plane publishes an operationId and nothing else.
|
||||
func TestEveryTypedOpIsInTheDocumentWithProse(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
routes(app, fakeService())
|
||||
doc, err := openapi.Spec(app, openapi.Info{Title: "deploy", Version: "v1"})
|
||||
if err != nil {
|
||||
|
||||
@@ -150,15 +150,12 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
//
|
||||
// Registration order is match order, and it is the order it has always been.
|
||||
func routes(app cloud.Router, zapp *zip.App, s *cloud.Service[state]) {
|
||||
// The bridge FIRST, bounded to destinations' own subtree: a typed op receives
|
||||
// only a context, so the validated org has to be parked there, and fiber runs
|
||||
// middleware in registration order — one installed after these leaves would
|
||||
// never run. cloud.Listen installs one app-wide too; nesting is harmless (the
|
||||
// inner one is what the handler sees), and having it here is what makes this
|
||||
// package's own tests — which mount on a bare zip.App — exercise the same
|
||||
// tenancy the binary does.
|
||||
// A typed op receives only a context, so the validated org has to be parked
|
||||
// there. cloud.Bridge parks it, and the COMPOSER installs it, not this
|
||||
// subsystem: the fused host once at its root (serve.go), and a plugin
|
||||
// program's constructor likewise. An install here would only repeat the one
|
||||
// the program already carries.
|
||||
g := app.Group("/v1/destinations")
|
||||
g.Use(cloud.Bridge())
|
||||
|
||||
o := ops{s: s}
|
||||
zip.Get(zapp, "/v1/destinations", o.list)
|
||||
|
||||
@@ -36,13 +36,18 @@ var untypedByDesign = map[string]string{
|
||||
"and a CLI command — not a document claiming it takes no body.",
|
||||
}
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge
|
||||
// (routes says why): the program's composer owns it — serve.go in production — so
|
||||
// a test app owes the same install, else every org-scoped op answers 403 for a
|
||||
// reason no composed program has.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mountApp mounts the destinations surface on a fresh in-memory app with a temp
|
||||
// store, exactly as the unified binary does — and, deliberately, with NO app-wide
|
||||
// cloud.Bridge, so the bridge these ops read their tenant through has to be the
|
||||
// one Mount installs itself.
|
||||
// store, exactly as the unified binary does: composed at the root, then mounted.
|
||||
func mountApp(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
t.Setenv(publicFanoutEnv, "0") // a document is not a reason to install a live sink
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("test"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
|
||||
+5
-10
@@ -150,16 +150,11 @@ type ops struct{ s *cloud.Service[state] }
|
||||
// groups, because joining a "/v1/vpcs" prefix with an empty leaf yields
|
||||
// "/v1/vpcs/" — a different path from the one this surface has always served.
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
// cloud.Bridge carries into a typed op the request its signature drops — this
|
||||
// subsystem resolves its tenant through tenant(), which reads the validated
|
||||
// principal AND the SuperAdmin bit, so it needs the request itself and not
|
||||
// only the org. On the scoped Router this installs once per DECLARED prefix
|
||||
// (/v1/vpcs, /v1/balancers) and nowhere else. It must precede the leaves
|
||||
// below: fiber runs middleware in registration order, so one installed after
|
||||
// them never runs for them. Serve installs one app-wide too — nesting is
|
||||
// harmless (the inner one is what the handler sees) and the tests mount this
|
||||
// subsystem on a bare app with no Serve, so this install is what makes them pass.
|
||||
app.Use(cloud.Bridge())
|
||||
// cloud.Bridge belongs to the composer — the fused host installs it at its
|
||||
// root, and a plugin program's constructor does the same — so it is already
|
||||
// in place when these ops run. This package only reads what it parks:
|
||||
// tenant() resolves the validated principal and the SuperAdmin bit from the
|
||||
// context.
|
||||
zapp := cloud.ZipApp(app)
|
||||
if zapp == nil {
|
||||
s.Log.Error("do: router exposes no op registry; the DigitalOcean surface would serve routes no projection knows")
|
||||
|
||||
@@ -95,9 +95,16 @@ func (f *fakeLBs) Delete(_ context.Context, id string) (*godo.Response, error) {
|
||||
|
||||
// ── harness ──────────────────────────────────────────────────────────────────
|
||||
|
||||
// compose gives the test app what every real composer gives its program: the
|
||||
// fused host installs cloud.Bridge at its root and a plugin program's
|
||||
// constructor does the same, so a bare test app that skipped it would answer
|
||||
// 403 for a reason production can never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func mountFake(t *testing.T) (*zip.App, *fakeVPCs, *fakeLBs) {
|
||||
t.Helper()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
vpcs := &fakeVPCs{byID: map[string]*godo.VPC{}}
|
||||
lbs := &fakeLBs{byID: map[string]*godo.LoadBalancer{}}
|
||||
s := &cloud.Service[state]{Base: cloud.NewBase(cloud.Deps{Logger: luxlog.New("test")}, "do"), State: state{vpcs: vpcs, lbs: lbs}}
|
||||
@@ -260,6 +267,7 @@ func TestForgePathRefused(t *testing.T) {
|
||||
|
||||
// X-Org-Id present but NO X-User-Id (the forgeable path) → still 403.
|
||||
app2 := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app2)
|
||||
s := &cloud.Service[state]{Base: cloud.NewBase(cloud.Deps{Logger: luxlog.New("test")}, "do"), State: state{vpcs: &fakeVPCs{byID: map[string]*godo.VPC{}}, lbs: &fakeLBs{byID: map[string]*godo.LoadBalancer{}}}}
|
||||
routes(app2, s)
|
||||
rq := httptest.NewRequest(http.MethodGet, "/v1/vpcs", nil)
|
||||
@@ -278,6 +286,7 @@ func TestForgePathRefused(t *testing.T) {
|
||||
// honest 503 — never a fabricated resource.
|
||||
func TestFailClosedWhenUnconfigured(t *testing.T) {
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
s := &cloud.Service[state]{Base: cloud.NewBase(cloud.Deps{Logger: luxlog.New("test")}, "do"), State: state{}} // nil seams → unconfigured
|
||||
routes(app, s)
|
||||
|
||||
|
||||
@@ -160,10 +160,9 @@ func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group("/v1/engine")
|
||||
o := ops{s: s}
|
||||
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated
|
||||
// principal reaches it by being parked there — never as an In field.
|
||||
g.Use(cloud.Bridge())
|
||||
|
||||
// A typed op receives only a context, so the validated principal reaches it
|
||||
// parked there by cloud.Bridge — never as an In field. The composer owns
|
||||
// that install, once at its root; this group is a bare path prefix.
|
||||
zip.Get(g, "/status", o.status,
|
||||
zip.WithOperationID("engineStatus"),
|
||||
zip.WithSummary("Whether the serving runtime is reachable, and which build it runs"))
|
||||
|
||||
@@ -34,6 +34,7 @@ func liveApp(t *testing.T) *zip.App {
|
||||
t.Setenv("ENGINE_UPSTREAM", up)
|
||||
t.Setenv("ENGINE_API_KEY", os.Getenv("ENGINE_E2E_KEY"))
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("enginelive"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("enginelive"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -150,6 +150,13 @@ func (f *fakeEngine) handler() http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root. In a
|
||||
// test the test IS the composer, so it owes the same thing — a test that skips it
|
||||
// tests a program where every op answers 403 for a reason that would never exist
|
||||
// in production. Same helper apps/integrations uses.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// harness mounts the app against a fake upstream and returns both.
|
||||
func harness(t *testing.T) (*zip.App, *fakeEngine) {
|
||||
t.Helper()
|
||||
@@ -160,6 +167,7 @@ func harness(t *testing.T) (*zip.App, *fakeEngine) {
|
||||
t.Setenv("ENGINE_API_KEY", "svc-key-test")
|
||||
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("enginetest"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("enginetest"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
@@ -435,6 +443,7 @@ func TestStatusIsAnHonestLens(t *testing.T) {
|
||||
}
|
||||
|
||||
down := zip.New(zip.Config{Logger: luxlog.New("enginetest"), DisableStartupMessage: true})
|
||||
compose(down)
|
||||
t.Setenv("ENGINE_UPSTREAM", "http://127.0.0.1:1") // nothing listens
|
||||
if err := Mount(down, cloud.Deps{Logger: luxlog.New("enginetest"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
|
||||
@@ -116,15 +116,11 @@ type ops struct{ s *service }
|
||||
// calls it, and so do the package's tests, so a test can never exercise a router
|
||||
// this binary does not serve.
|
||||
func routes(app cloud.Router, s *service) {
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated org and the
|
||||
// request its admin gates read reach it by being parked there. fiber runs
|
||||
// middleware in registration order, so one installed after its leaves never runs.
|
||||
// Installed through the SUBSYSTEM's own router, which scopes it to the prefixes
|
||||
// this app declares — BOTH of them: this surface owns two top-level nouns
|
||||
// (/v1/entitlements and /v1/orgs/:org/entitlements), and the /v1/<name> default
|
||||
// MountPrefixes falls back to covers only the first, so plugin/entitlements/main.go
|
||||
// passes manifest.PrefixesFor("entitlements") rather than relying on it.
|
||||
app.Use(cloud.Bridge())
|
||||
// cloud.Bridge is not installed here: the composer installs it once at the
|
||||
// root, after the identity check that mints the validated org and before any
|
||||
// subsystem registers a route — an order only the whole program can assert.
|
||||
// The ops below read what it parks (the validated org, and the request their
|
||||
// admin gates inspect) off the context.
|
||||
|
||||
// Declared on GROUPS: the op's path is the prefix composed with the leaf, which
|
||||
// is the identity every projection keys on, and cmd/zipdoc resolves the prefix
|
||||
|
||||
@@ -70,13 +70,20 @@ func (f *fakeCommerce) CheckEntitlement(_ context.Context, orgID, productID stri
|
||||
|
||||
// ── harness ──────────────────────────────────────────────────────────────────
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root, after
|
||||
// the identity check that mints the validated org and before any subsystem
|
||||
// registers a route. In production that composer is serve.go. In a test the test
|
||||
// IS the composer, so it owes the same thing.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
func mount(t *testing.T, commerce cloud.CommerceClient) (*zip.App, *service) {
|
||||
t.Helper()
|
||||
s := &service{store: openTestStore(t), commerce: commerce, log: luxlog.New("test")}
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
// The REAL registration, not a reconstruction of it: a test that rebuilt the
|
||||
// router by hand could exercise a surface this binary does not serve — and
|
||||
// would silently drop the cloud.Bridge every typed op resolves its org through.
|
||||
// router by hand could exercise a surface this binary does not serve.
|
||||
routes(app, s)
|
||||
return app, s
|
||||
}
|
||||
|
||||
@@ -101,9 +101,8 @@ func TestEveryTypedOpIsDescribed(t *testing.T) {
|
||||
// pass fixed. This subsystem owns TWO top-level nouns, and the /v1/<name> default
|
||||
// MountPrefixes falls back to covers only ONE of them — so before the fix the
|
||||
// standalone binary attributed half its surface to no subsystem, and any middleware
|
||||
// the subsystem installed through its own router (including the typed-op Bridge
|
||||
// every op resolves its org through) landed on /v1/entitlements alone and never ran
|
||||
// for /v1/orgs/…. The apps/plan defect, in its partial form.
|
||||
// the subsystem installed through its own router landed on /v1/entitlements alone
|
||||
// and never ran for /v1/orgs/…. The apps/plan defect, in its partial form.
|
||||
//
|
||||
// It reads the MANIFEST — the same list plugin/entitlements/main.go passes — against
|
||||
// the routes the REAL mount serves, so adding a route outside both prefixes goes red
|
||||
@@ -143,8 +142,8 @@ func TestDeclaredPrefixesCoverEveryServedRoute(t *testing.T) {
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("%s is served but lies outside every declared prefix %v — cloud.Declare cannot "+
|
||||
"attribute it, and the subsystem's own middleware (the typed-op Bridge included) will "+
|
||||
"not run for it. Add the prefix to manifest/apps.go.", key, prefixes)
|
||||
"attribute it, and the subsystem's own middleware will not run for it. "+
|
||||
"Add the prefix to manifest/apps.go.", key, prefixes)
|
||||
}
|
||||
}
|
||||
// And the default the framework would fall back to must NOT be enough, so the
|
||||
|
||||
@@ -34,15 +34,10 @@ import (
|
||||
type ops struct{ s *cloud.Service[state] }
|
||||
|
||||
func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
// cloud.Bridge carries into a typed op the request its signature drops. This
|
||||
// surface needs more than the org key: the PROJECT scope that narrows within
|
||||
// it, and the actor an audited write is recorded under. On the scoped Router
|
||||
// this installs once per DECLARED prefix (/v1/flags) and nowhere else, and it
|
||||
// must precede the leaves below — fiber runs middleware in registration order.
|
||||
// Serve installs one app-wide too; nesting is harmless (the inner one is what
|
||||
// the handler sees) and the tests mount this subsystem on a bare app with no
|
||||
// Serve, so this install is what makes them pass.
|
||||
app.Use(cloud.Bridge())
|
||||
// cloud.Bridge is installed by whoever composes the app — the fused host at
|
||||
// its root — never here: the org, project scope and actor every op below
|
||||
// reads still arrive because the root install parks the request on the
|
||||
// context.
|
||||
zapp := cloud.ZipApp(app)
|
||||
if zapp == nil {
|
||||
s.Log.Error("flags: router exposes no op registry; the flag surface would serve routes no projection knows")
|
||||
|
||||
@@ -27,11 +27,20 @@ import (
|
||||
|
||||
var httpCfg = zip.TestConfig{Timeout: 10 * time.Second, FailOnTimeout: true}
|
||||
|
||||
// compose installs what a HOST installs. A subsystem never installs cloud.Bridge
|
||||
// (routes says why): the program's composer installs it once at the root, after
|
||||
// the identity check and before any route registers. In a test the test IS the
|
||||
// composer, so it owes the same install — a test that skips it does not test a
|
||||
// stricter program, it tests one where every org-scoped op answers 403 for a
|
||||
// reason that would never exist in production.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// mountHTTP puts the flag surface on a bare app over a temp-dir store tree.
|
||||
func mountHTTP(t *testing.T) *zip.App {
|
||||
t.Helper()
|
||||
c := newTestClient(t)
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test")})
|
||||
compose(app)
|
||||
svc := &cloud.Service[state]{
|
||||
Base: cloud.NewBase(cloud.Deps{Logger: luxlog.New("test")}, "flags"),
|
||||
State: state{client: c},
|
||||
|
||||
+4
-5
@@ -141,11 +141,10 @@ func routes(app cloud.Router, s *cloud.Service[state]) {
|
||||
g := app.Group("/v1/flow")
|
||||
o := ops{s: s}
|
||||
|
||||
// Bridge FIRST: a typed op receives only a context, so the validated
|
||||
// principal reaches it by being parked there — never as an In field, which
|
||||
// is caller-supplied and would be a cross-tenant read the caller asserted
|
||||
// for itself.
|
||||
g.Use(cloud.Bridge())
|
||||
// cloud.Bridge is not installed here: the composer owns it — the fused
|
||||
// host installs it once at its root, and the plugin constructor does the
|
||||
// same for a plugin program — and typed ops read the validated principal
|
||||
// it parks on the context.
|
||||
|
||||
zip.Get(g, "/status", o.status)
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ func liveApp(t *testing.T) *zip.App {
|
||||
t.Setenv("FLOW_UPSTREAM", up)
|
||||
t.Setenv("FLOW_API_KEY", os.Getenv("FLOW_E2E_KEY"))
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("flowlive"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("flowlive"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
|
||||
@@ -183,6 +183,13 @@ func (f *fakeFlow) handler() http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
// compose installs what a host installs. A subsystem never installs cloud.Bridge
|
||||
// (routes() says why): the program's composer installs it once at the root, and
|
||||
// in a test the test is the composer, so it owes the same install. A test that
|
||||
// skips it does not test a stricter program — it tests one where every op that
|
||||
// reads the principal answers a refusal production can never produce.
|
||||
func compose(app *zip.App) { app.Use(cloud.Bridge()) }
|
||||
|
||||
// harness mounts the app against a fake upstream and returns both.
|
||||
func harness(t *testing.T) (*zip.App, *fakeFlow) {
|
||||
t.Helper()
|
||||
@@ -193,6 +200,7 @@ func harness(t *testing.T) (*zip.App, *fakeFlow) {
|
||||
t.Setenv("FLOW_API_KEY", "svc-key-test")
|
||||
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("flowtest"), DisableStartupMessage: true})
|
||||
compose(app)
|
||||
if err := Mount(app, cloud.Deps{Logger: luxlog.New("flowtest"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
}
|
||||
@@ -448,6 +456,7 @@ func TestStatusIsAnHonestLens(t *testing.T) {
|
||||
}
|
||||
|
||||
down := zip.New(zip.Config{Logger: luxlog.New("flowtest"), DisableStartupMessage: true})
|
||||
compose(down)
|
||||
t.Setenv("FLOW_UPSTREAM", "http://127.0.0.1:1") // nothing listens
|
||||
if err := Mount(down, cloud.Deps{Logger: luxlog.New("flowtest"), DataDir: t.TempDir()}); err != nil {
|
||||
t.Fatalf("Mount: %v", err)
|
||||
|
||||
@@ -93,12 +93,17 @@ func Mount(app cloud.Router, deps cloud.Deps) error {
|
||||
|
||||
g := app.Group("/v1/framework")
|
||||
|
||||
// The bridges, FIRST: a typed op receives only a context, so every request
|
||||
// fact its signature drops has to be parked there — the validated org by
|
||||
// cloud.Bridge, the two header-only identity facts by bridgeFacts. fiber runs
|
||||
// middleware in registration order, so one installed after its leaves never
|
||||
// runs; and the group bounds them to the subtree this subsystem serves.
|
||||
g.Use(cloud.Bridge(), zip.H(bridgeFacts))
|
||||
// bridgeFacts FIRST: a typed op receives only a context, so the two
|
||||
// header-only identity facts its signature drops have to be parked there.
|
||||
// fiber runs middleware in registration order, so one installed after its
|
||||
// leaves never runs; and the group bounds it to the subtree this subsystem
|
||||
// serves.
|
||||
//
|
||||
// cloud.Bridge is not installed here. Whoever composes the program installs it
|
||||
// once at the root — after the identity check that mints the validated org and
|
||||
// before any subsystem registers a route (serve.go) — because that order is a
|
||||
// property of the whole program and no subsystem can assert it for itself.
|
||||
g.Use(zip.H(bridgeFacts))
|
||||
|
||||
o := ops{s: s}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user