Why does constrain by X and Y fail when both extend SecurityGate?

← Web Services · Ref: Q1295

When 'constrain by X and Y' names two functions that both extend the same base (both SecurityGate or both CORSPolicy), the compiler emits E12064.

WHAT CAUSES E12064

Duplicate roles in a two-reference constraint:

  constrain by GateAlpha and GateBeta

If both GateAlpha and GateBeta extend SecurityGate, the roles are duplicated.

WHY ONE OF EACH

The runtime applies the two constraints differently:

  SecurityGate: called with HTTPContext, authenticates the request
  CORSPolicy: called with origin String, validates cross-origin access

Two gates or two policies create ambiguity — which one runs first? What happens if they disagree?

CORRECT PATTERN

One SecurityGate + one CORSPolicy:

  constrain by JwtGate and AllowedOrigins

The router knows exactly which function handles authentication and which handles CORS.

SINGLE CONSTRAINT IS FINE

If you only need authentication without CORS, write 'constrain by JwtGate'. Or only CORS without authentication (unusual), write 'constrain by MyCors'.

See Q1292 for security posture overview. See Q1293 for constraint resolution. See Q1294 for constraint type validation.

Example

defines module qa.webdeep.duplicaterole

  defines constant

    jsonType <- "application/json"
    noCache <- "no-cache"
    langEn <- "en"
    okStatus <- 200

  defines function

    <?-
      SecurityGate for JWT token validation.
    -?>
    JwtGate is SecurityGate
      -> context as HTTPContext
      <- rtn as HTTPContext: context

    <?-
      CORSPolicy that allows known origins.
    -?>
    KnownOrigins is CORSPolicy
      -> origin as String
      <- rtn as Boolean: true

  defines service

    <?-
      Correct: one SecurityGate + one CORSPolicy.
      The router knows which function handles which concern.
    -?>
    ValidDualConstraint :/api/dual constrain by JwtGate and KnownOrigins

      resource() as GET for :/resource
        <- response as HTTPResponse: () with trait HTTPResponse
          override content()
            <- rtn as Bytes: Bytes().withUtf8(`{"dual": "gate+cors"}`)
          override status() as pure
            <- rtn as Integer: okStatus
          override contentType() as pure
            <- rtn as String: jsonType
          override cacheControl() as pure
            <- rtn as String: noCache
          override contentLanguage() as pure
            <- rtn as String: langEn
          default operator ?

  defines application

    DuplicateRoleApp
      register ValidDualConstraint()

  defines program

    DuplicateConstraintRoleDemo()
      stdout <- Stdout()
      stdout.println("Duplicate constraint role rules:")
      stdout.println("  'constrain by Gate and Cors' -> valid (different roles)")
      stdout.println("  'constrain by Gate1 and Gate2' -> E12064 (both SecurityGate)")
      stdout.println("  'constrain by Cors1 and Cors2' -> E12064 (both CORSPolicy)")
Other ways to ask this
  • What is E12064 duplicate constraint role?
  • Can I use two SecurityGate functions in constrain by?
  • Why must the two constraint references have different roles?
  • How do I combine SecurityGate and CORSPolicy in one clause?

Coming from another language?

Java: Spring Security allows chaining multiple filters of the same type — order is explicit in filter chain. Python: multiple decorators stack. Go: middleware wraps in order. Rust: Actix Transform chain. EK9: at most one SecurityGate and one CORSPolicy per constraint clause, enforced at compile time. No ambiguous filter ordering.

Keywords: CORSPolicy, service, role, constrain, E12064, http, SecurityGate, duplicate