feat(frontends,security,api,ui): a frontend can hold a token scope of its own

This commit is contained in:
hh
2026-09-06 23:26:26 +02:00
parent 5401cbe187
commit d5aaa80822
11 changed files with 245 additions and 21 deletions
+9
View File
@@ -10,6 +10,8 @@ from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
from beaver_gateway.security.auth import BUILTIN_SCOPES
if TYPE_CHECKING:
from collections.abc import Awaitable, Callable, Mapping, Sequence
@@ -54,6 +56,8 @@ class GatewayRuntime:
scheduler: Any = None
public_url: str | None = None
"""``Gateway.public_url``; ``None`` derives the origin from the request."""
scopes: frozenset[str] = BUILTIN_SCOPES
"""Every token scope this gateway knows: the builtins plus each frontend's."""
class Frontend(ABC):
@@ -64,6 +68,10 @@ class Frontend(ABC):
is for non-HTTP work (polling, vault mirrors) and defaults to nothing.
``landing`` marks the app that ``/`` redirects to.
``scope`` is the token scope this frontend's routes are gated by; a
frontend that names its own scope gets keys nobody else's token opens,
and ``POST /api/tokens`` will mint them (see ``security/auth.py``).
A frontend that shows conversations declares ``name`` (the binding
key) and ``kinds`` (which conversation kinds it shows). The first
frontend whose ``materialize`` returns a binding is the *home* of
@@ -76,6 +84,7 @@ class Frontend(ABC):
kinds: tuple[Kind, ...] = ()
path: str | None = None
landing: bool = False
scope: str | None = None
@abstractmethod
def configure(self, runtime: GatewayRuntime) -> None: ...