Skip to content

Separate management-plane auth from data-plane auth (platform operator credential) #359

Description

@EricAndrechek

Problem

WaveHouse validates exactly one data-plane token verifier per instance — one auth.jwks_url or one auth.jwt_secret — and the entire /v1/admin/* surface is gated by a role claim inside tokens from that same issuer (the admin_role bypass). That conflates two genuinely different concerns:

  • Data-plane identity — who the callers are (a deployer's end-users / devices / services). In a managed or multitenant deployment this issuer belongs to the customer: their own IdP's JWKS, an HMAC secret they control, or public + a default_role.
  • Platform / management operations — applying config, seeding/editing policy, managing pipes, schema refresh, viewing the DLQ, admin/query. In a managed deployment these are performed by the platform operator (a hosting control plane), a different principal than the customer's IdP — and one that cannot mint a token the customer's issuer would sign.

Today there is no way for a platform operator to administer an instance whose data-plane verifier is the customer's IdP — the operator holds no token WaveHouse will accept. Relevant code:

  • internal/auth/auth.go — single JWKS-or-HMAC verifier; no iss/aud checks; role read from auth.role_claim.
  • internal/policy/roles.go — admin_role bypasses all policy.
  • internal/api/router.go — admin routes gated by that role.

This blocks managed-hosting / BYO-IdP scenarios, and is also a prerequisite for any future multitenant WaveHouse (management-plane vs data-plane auth separation is the same split).

Proposed Solution

Add a management/operator credential that authorizes the management surface independently of the data-plane JWT verifier:

  • A config-set operator credential — start with a static admin API key (e.g. WH_AUTH_OPERATOR_KEY, presented via a dedicated header), with mTLS client-cert as a later, stronger option.
  • A request bearing it is treated as platform-operator (full management) regardless of the JWT issuer / role-claim path — which continues to work unchanged for data-plane callers (including a customer's own admin_role users hitting pipes/DLQ/policy via their IdP).
  • Scope it to the management surface (policy CRUD, pipes CRUD, schema refresh, DLQ stats, admin/query), not the data path.

Net effect: data-plane auth stays 100% the customer's (their JWKS / HMAC / public + role policy), while a platform operator can administer the instance with a credential the customer's issuer need not know about.

Alternatives Considered

  • Federating auth gateway (the operator's proxy validates the customer's tokens and re-mints WaveHouse-trusted ones): avoids any engine change, but puts token validation + re-minting on the hot path, requires the proxy to hold per-tenant signing keys, and becomes a high-blast-radius always-on service — the opposite of a thin, independent data path. Rejected in favor of a small, surgical engine change.
  • A second JWKS/HMAC verifier reserved for admin: workable, but an API key / mTLS is simpler to provision and rotate for a machine principal and doesn't require the operator to stand up an issuer.

Additional Context

Follow-on refinement worth scoping separately: today admin_role is all-or-nothing and includes raw-SQL passthrough (POST /v1/admin/query). To let a deployer grant, say, "create a Pipe" or "view the DLQ" to their own privileged users without also granting arbitrary SQL, consider capability-scoped admin permissions (pipe-mgmt / policy-mgmt / dlq-view / schema / raw-sql) that roles can be granted subsets of. Suggested sequencing: step 1 = separate management auth from data auth (this issue); step 2 = capability scoping.

This comes out of designing WaveHouse Cloud's control plane (a managed, multitenant hosting layer). Filing for visibility and discussion — happy to refine the scope or split steps 1/2 into separate issues.

Activity

  1. added
    enhancementNew feature or request
    area/apiHTTP handlers, routing, middleware
    area/policyAccess control policies (Hasura-style)
    on Jun 30, 2026
  2. coderabbitai commented on Jun 30, 2026

    @coderabbitai
    ⚠️ Possible Duplicate Issue(s)
    🔗 Related PRs

    #123 - fix(api): drop CORS credentials + skip same-origin decoration [merged]
    #172 - feat(rbac)!: fail-closed authorization + default_role public access [closed]


    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  3. moved this from Backlog to In progress in WaveHouse Task Boardon Jul 6, 2026
  4. moved this from In progress to Done in WaveHouse Task Boardon Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/apiHTTP handlers, routing, middlewarearea/policyAccess control policies (Hasura-style)breaking-changeBreaking change to public API, CLI, or configenhancementNew feature or request

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions