Authentik 2025.8 ships a default 'groups' scope that emits the user's
ak_groups as the 'groups' claim automatically. Adding a custom scope
mapping with the same scope_name causes Authentik to concatenate
both emitters' output, producing duplicated group names in the
users.groups column.
Document the symptom and the fix, plus a sibling note that Keycloak
needs the opposite (explicit mapper required to emit anything).
Add a Groups column on the User model populated from the OIDC
'groups' claim at login, and surface it through the gRPC/REST
User message so external tools (Headplane) can read group
membership without reaching into the database.
- proto: add 'repeated string groups = 9' to v1.User.
- types.User: Groups text column, GetGroups/SetGroups JSON helpers,
FromClaim populates from claims.Groups. The existing
FlexibleStringSlice on OIDCClaims.Groups already handles
JumpCloud-style single-string emission.
- types.User.Proto(): populate v1.User.Groups via GetGroups().
- db migration 202505141323: add the column BEFORE the existing
202505141324 migration that loads users via the struct, so the
schema is in place before any migration touches the User type.
- db migration 202507021200: extend the inline CREATE TABLE users
and INSERT INTO users ... SELECT FROM users_old to carry the
new column through the SQLite schema-recreation step.
- schema.sql: declare the column so squibble.Validate accepts
databases produced by the new migration chain. Verified against
all 7 historical sqlite dumps in hscontrol/db/testdata/sqlite.
- types.UserView, types_clone.go: regenerated to expose Groups.
- config-example.yaml, docs/ref/oidc.md: note the 'groups' scope
and the role the column plays for external integrations.
- integration/oidc_groups_test.go: verify the round-trip via
headscale.ListUsers() for users with multi-group, single-group,
and empty group memberships.
- Mention policy as generic term that covers ACLs or Grants
- Refresh routes policy examples
- Remove Headscale specific exit node separation. Use via instead.
Fixes: #3087
- Rename from acl.md to policy.md and setup redirect links
- Mention both ACLs and Grants
- Remove most old ACL docs and replace with links to Tailscale docs
- Add "Getting started" section
- Add section about notable differences
The directory /usr/share/doc/headscale/examples may be used to install
arbitrary example files. This is useful to get a matching configuration
for the release which gets also overwritten automatically.
Introduce a structured NodeConfig that replaces the flat
EphemeralNodeInactivityTimeout field with a nested Node section.
Add node.expiry config (default: no expiry) as the unified default key
expiry for all non-tagged nodes regardless of registration method.
Remove oidc.expiry entirely — node.expiry now applies to OIDC nodes
the same as all other registration methods. Using oidc.expiry in the
config is a hard error. determineNodeExpiry() returns nil (no expiry)
unless use_expiry_from_token is enabled, letting state.go apply the
node.expiry default uniformly.
The old ephemeral_node_inactivity_timeout key is preserved for
backwards compatibility.
Updates #1711
Move the container image and binary download details from the README
into a dedicated documentation page at setup/install/main. This gives
development builds a proper home in the docs site alongside the other
install methods. The README now links to the docs page instead.
The project mkdocs-material is in maintenance-only mode and their
successor is not ready yet.
Use the modern, refreshed theme and drop the pymdownx.magiclink
extension.
This just fixes a small issue I noticed reading the docs: the two 'scenarios' listed in the scaling section end up showing up as a numbered list of five items, instead of the desired two items + their descriptions.
Just the tags tag:router and tag:exit are owned by alice. Upon join,
those nodes will have their ownership transferred from alice to the
system user "tagged-devices".