Skip to content

v0.28.0

Choose a tag to compare

@angel-ip angel-ip released this 03 Oct 09:28
· 560 commits to main since this release
26a12b7

@kanzo-tech/auth

Roles inside an organization come from organization[alias].resource_access[clientId].roles, and group paths are no longer read.

  • session.organizations[n].roles is now what Keycloak writes inside each organization entry: the
    roles an organization's administrator mapped onto the groups the person is in, with composite
    roles expanded. The realm needs the Organization Group Membership mapper on the organization
    scope with addGroupRoleMappings on, which needs Keycloak 26.7 or later; without it every
    organization reads as a membership holding no roles. services/auth in this repository already
    configures it.
  • Group names are not read at all, so a group called /board/editor grants nothing any more. If
    your realm named groups /<client>/<role>, map the client role onto the group instead (the
    console, or services/auth/scripts/map-group-role.sh ALIAS GROUP CLIENT ROLE); the group can then
    be called anything.
  • roleFromGroupPath is removed. Delete any call to it: organizationOf(session, alias)?.roles
    is already the roles for your client.
  • An organization's roles are still not merged into session.roles.
  • If you wrote a hierarchy out at the call site (can(s, "owner", org) || can(s, "member", org)),
    declare it as composite roles in your client registration instead and ask for the lowest role
    once: the token carries the expanded set.

The realm this package reads now ships beside it, in services/auth. Keycloak 26.8 with the
platform realm as code: one realm, organizations as tenants, and the claim mappers set so that an
application's roles in an organization arrive as organization[alias].resource_access[clientId].roles,
composites expanded, on both tokens. docker compose up -d --wait in that directory gives you a
working login; scripts/verify.sh checks the claims on a live token.

Register your application from your own repository with the modules/app Terraform module — client,
audience and roles, a hierarchy declared once as composites — pinned to the release you build against:

source = "git::https://github.com/Kanzo-Tech/ui.git//services/auth/modules/app?ref=v0.28.0"

and map your roles onto an organization's groups with scripts/map-group-role.sh. Nothing in the
realm names an application.

@kanzo-tech/graph

GraphStatus says where the graph is in one word, and GraphCounts stops saying "drawn".

  • New part GraphStatus: a badge with a status dot that reads Loading, Laying out 42%, Ready or
    Failed. It folds the data's status and the layout's motion and progress together, so a
    running layout over a loaded graph no longer shows as "idle". If your footer drew its own badge
    from useGraphState((s) => s.status), delete it and render <GraphStatus />. The word is a polite
    live region; the badge is aria-busy while loading or laying out.
  • GraphCounts now reads "5K nodes · 8K edges" with no filter and "1.2K of 5K nodes match · 3K edges"
    under the page's filter, instead of "1.2K of 5K nodes drawn · 8K edges". A test or a snapshot that
    matched the old sentence needs the new one. The spinner prop is gone: remove it, and put
    <GraphStatus /> beside the counts where you relied on the spinner.
  • GraphState has a new field, matching: how many vertices of the corpus the page's filter keeps,
    or null when nothing is filtered.
  • The type GraphStatus is renamed DataStatus, because the part takes the name. Edit
    import type { GraphStatus } to import type { DataStatus }; the values are unchanged.

@kanzo-tech/llm

The gateway createGateway talks to now ships beside it, in services/ai. LiteLLM serving the
chat and complete aliases: docker compose up -d --wait there runs both on open models through
Docker Model Runner, offline. For a deployment, modules/gateway runs it as Swarm services with your
own profile (which upstream answers each alias) and keys, and modules/team gives each of your
tenants a budget and the key its server presents.