Skip to content

Support namespace scoping via JWT claims #2217

@angel-manuel

Description

@angel-manuel

Problem

When running sqld with --enable-namespaces in a multi-tenant setup, there's no way to restrict a JWT token to a specific namespace (or set of namespaces) using claims. Currently:

  • A valid JWT grants access to all namespaces at the specified access level (rw or ro)
  • The namespace is selected by the client via the x-namespace header
  • A compromised token can access any namespace

Existing Work

Issue #500 proposes per-namespace signing keys, which solves key isolation but requires managing N keys for N namespaces. This proposal is complementary — it uses a single signing key with claim-based scoping.

Proposal

Add support for an optional ns (or namespace) claim in the JWT payload that restricts which namespace(s) the token can access:

// Token scoped to a single namespace
{
  "a": "rw",
  "ns": "workspace-abc123",
  "exp": 1710200000
}

// Token scoped to multiple namespaces
{
  "a": "ro",
  "ns": ["workspace-abc123", "workspace-def456"],
  "exp": 1710200000
}

// Token with no ns claim = current behavior (access all namespaces)
{
  "a": "rw",
  "exp": 1710200000
}

When ns is present, sqld would validate that the requested namespace (from x-namespace header, host header, or gRPC metadata) matches the claim. Mismatches return 403.

Use Case

We're building a platform where ephemeral VMs each own a workspace backed by a sqld namespace. An orchestrator mints short-lived JWTs for each VM. Without namespace scoping in the JWT, a compromised VM could push/pull to any workspace. With this feature, each VM's token is locked to its own namespace.

This is simpler to operate than per-namespace keys (#500) when there's a single trusted token issuer (our orchestrator) but many namespaces.

Backward Compatibility

Fully backward compatible — tokens without the ns claim behave exactly as today.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions