Host-neutral help (deployment-specific facts removed), registered with
HelpBundles, gated by HelpBundleAssertions. QQQ 4.1.0-RC.7.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add help/qbit-authentik.yaml (QQQ help-bundle format) for the Identity app and
all 15 tables, applied by AuthentikHelp (STAND-IN for core #1198). API-backed
tables say rows are read live and what an empty list means. Add
AuthentikTablesGuardrailTest (used at runtime, explained, documented).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move to the released 4.1.0-RC.6, which includes core
TableFeaturePermissionsMetaData (QRun-IO/qqq#1136). The policy catalog
discovers features through core only; the test-only injection overload
is removed. The policy feature tests register real metadata instead of
the synthetic catalog, so the expired STAND-IN marker is gone.
Adopt qbit-build-parent 2.1.0 with the guardrails enforced and strict
raw-route mode.
Closes#20 (architecture review QRun-IO/qqq#1150, finding F19).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Scopes: Create Token takes read only, apps and tables when
tokenScopesEnabled (validated against the instance and the operator's
own access). AuthentikTokenScopes reads the signed qqq_token claim in
both mappers (and AuthentikTokenScopes.apply for hosts with their own
customizer): no row = unscoped, deleted token = no permissions, any
verification failure = no permissions. STAND-IN for core
QCredentialScope (qqq#1168, RC.7): permissions are intersected; the
core scope is attached by reflection when present (empty dimension =
null = unrestricted).
- Last used from Authentik login events (method token, 90 days, cached
5 minutes); createdAt and scopeSummary from the scope row.
- Daily expiry reminders (7 days, 1 day) through the optional Alerts
producer, deduplicated on the scope row; guard-bypass alert.
- Admin widget on the app home (authentik.tokens.manage): by owner, by
age, unused for tokenUnusedDays, deactivated owners; bulk Revoke Token
on the list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TokenContractIT (9 tests) against the production token blueprint
(verbatim copy of cluster-gitops kof22-qqq-tokens.yaml): caller-owned
create, PATCH defaults, expiry cap (brand, groups, user), token
exchange as the user via the broker, the deny policy for protected
users, the qqq_token claim (profile only), service_account + set_key +
PATCH, the detective alarm, and the qbit lifecycle with the one-time
key absent from audit rows and events.
- Findings now in DESIGN.md: service_account and set_key need add_token
(safe: a non-superuser create is always the caller's); set_key events
use the model reference shape.
- Service-account lifetime cap is a user attribute
(serviceAccountTokenLifetime, default days=365), not a group.
- Test fixtures carry the production role; SuperuserFloorIT accepts the
400 from /recovery/ with reset_user_password (the qbit never calls it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TOKENS feature, on by default with writes (AUTHENTIK_QBIT_TOKENS=false
opts out). Create, rotate, revoke and extend tokens, and create service
accounts, as record and table actions with the standard Review/Result.
- People's tokens are created as that person through the token broker
(RFC 8693 exchange); service accounts through service_account, set_key
and PATCH (always intent, user and expires, with a race guard).
- Guardrails: protected owners (superusers, authentik Admins,
identity-admins, qqq.io/protected, the qbit account, configurable
groups), self-only for people, required capped expiry, optional
two-person approval for service-account tokens.
- One-time key via core one-time process output (stand-in until
qqq#1162 ships; refuses at Review when unavailable), never in tables,
audit, events, logs or process state; usage text from config.
- authentik_token_scope table; authentik.tokens.manage permission.
- In-memory Authentik models the 2026.8.3 token semantics.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the core baseline from 4.1.0-RC.2 to the released 4.1.0-RC.5. No
source changes were needed. Comments now reflect core's opt-in permission
enforcement in 4.1. The synthetic feature-permission catalog in tests is
tagged STAND-IN(core=QRun-IO/qqq#1136, removeBy=4.1.0-RC.6), because
TableFeaturePermissionsMetaData is on develop but not in RC.5.
Refs #20 (architecture review QRun-IO/qqq#1150, finding F19).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner decisions (2026-10-13) 23-30: tokens managed in the app and stored
in Authentik, shown once, self-service through Preferences, on by default
with an opt-out, guardrails (a)-(f), optional per-token scopes enforced
through core QCredentialScope (qqq#1153), reminders, last used, admin
views and bulk revoke.
Decision 30 records the owner-approved mechanism revision: Authentik
2026.8.3 always creates a token for the caller unless it is a superuser,
so people's tokens are created as that person through RFC 8693 token
exchange (token broker with a deny policy), service accounts through
service_account, set_key and PATCH. The qbit role gains change_token and
set_token_key and never holds add_token or view_token_key.
Adds docs/ACCESS-TOKENS.md ("Using access tokens").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalog lists each table's feature permissions, read generically from
PermissionsHelper.getAllAvailablePermissions (core
TableFeaturePermissionsMetaData), as a features array per table resource.
Policies grant them by name; the * wildcard still means the five
standard actions only, so existing policies grant nothing new.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Edit User: username (validated, unique) and other attributes (JSON object;
qqq.io/ and goauthentik.io/ keys kept and refused; blank keeps them; needs
authentik.audit.read, which also gates showing them). Edit Group: other
attributes the same way.
- Delete User and Delete Group behind the new DELETE feature (off by default;
Authentik delete_user/delete_group): typed confirmation, never yourself, a
protected account, a superuser group or the last identity administrator; a
group needs no members and no child groups; its policy row goes with it.
- Tests on the in-memory Authentik and a delete contract test on 2026.8.3;
README, STATUS, CHANGELOG, CLAUDE.md invariant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Users can change their own email in Authentik and emails are not unique, so
bootstrapOperators (emails) let a user become an identity operator by setting
their email to the owner's. Replaced by bootstrapOperatorSubjects: token sub
(email-looking subs ignored) or, with a verified email, the single active user
with that email whose pk or uuid is configured. Emails in config fail validation.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Permissions follow the owner's full-name rule (2026-10-12; spec #1065
amended in 8ee607610): identity.users.read -> authentik.users.read, etc.,
same suffixes; the wildcard is authentik.*.
- IdentityPermission names and WILDCARD renamed; @Deprecated
LEGACY_PREFIX, LEGACY_WILDCARD and getLegacyShortName() keep the old
names for upgrade scripts. Legacy names are never granted: the resolver
reports them as unknown and the drift report warns on them.
- UPGRADE.md: rename table, update qqq.io/permissions group attributes
(UI or blueprint) before deploying, sample SQL for host-stored grants,
sign users out afterwards.
- Code comments, drift messages, test fixtures, the test bootstrap
blueprint, README, CHANGELOG and docs/DESIGN.md use authentik.*.
The identity-admins group and the "Identity" app label are unchanged.
- Tests: IdentityPermissionTest (names, session form, legacy names not
resolved), resolver test that legacy grants are unknown and grant
nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Table names are now lowercase snake_case prefixed with the qbit name:
authentik_user, authentik_group, authentik_group_member,
authentik_session, authentik_event (were authentikUser, ...).
- AuthentikQBitConfig.applyTablePrefix(name) = snake(prefix) + "_" +
snake(name), plus a toSnakeCase helper. applyPrefix keeps the camelCase
join for apps, widgets, the backend and processes (authentikAdmin,
authentikApi), as spec #1065 says.
- @Deprecated AuthentikTables.LEGACY_*_TABLE_NAME constants hold the old
names for upgrade scripts.
- UPGRADE.md covers stored references (permissions, saved views, audit
table names, links). API-backed tables, so no DDL.
- README, CHANGELOG and docs/DESIGN.md use the new names.
- Tests: snake helper, prefix composition (default, empty, custom
camelCase prefix), producer and contract tests on the new names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(readme): add Adopting in another app; neutral examples (#3)
Adds the adoption table proposed in #3, verified against the config and
metadata producers, linking the QQQ adoption checklist. README and Javadoc
examples use neutral names (my-app-admin, my-app-prod); the kof22 test
blueprints are described as one host's example.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(changelog): record the adoption section (#3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: phase 0 foundations (read-only client, contract tests on Authentik 2026.8.3)
- AuthentikQBitConfig (validated, secrets resolved by the qbit, never
serialized or printed) and AuthentikQBitProducer (API backend, read-only
authentikUser / authentikGroup tables, Identity app).
- AuthentikActionUtil on qqq-backend-module-api: bearer auth, page/page_size
paging with pagination.count, key lookups by GET, server-side user scope,
AuthentikFilterMapper (rejects whatever Authentik cannot evaluate),
10 rps token-bucket throttle, 429/5xx retries, error mapping, and logging
that never carries the token, query strings, successful GET bodies or
sensitive bodies (AuthentikRedactor).
- AuthentikGuard: identity.* read checks, sensitive fields behind
identity.audit.read, all writes refused in this release, protected-account
detection.
- Pinned Authentik 2026.8.3 schema, SchemaDriftTest / SchemaDriftIT over
the API surface used, and a weekly drift workflow against new releases.
- Testcontainers harness (Authentik server + worker, Postgres, Mailpit) with
the kof22-shaped blueprints, fixture recorder, and contract tests (b), (c),
(d), (h) plus the access-check, sign-in policy, webhook and token findings.
- Owner checklist, rehearsal script and blueprint lint for the cluster-gitops
migration (KofTwentyTwo/cluster-gitops#72).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: phase 1 read-only MVP (memberships, sessions, events, effective permissions, drift report)
- authentikGroupMember: virtual membership table served from a group
(include_users), a user (groups_obj) or a bounded sweep; filters by group
pk/name, user pk/username or key; user scope checked per member.
- authentikSession and authentikEvent, behind identity.audit.read.
- GroupGraph + EffectivePermissionResolver: groups and inherited parents to
QQQ permissions; identity.* and catalog wildcards expanded, unknown names
reported and never granted. Groups show inherited superuser status and
effective permissions; users show effective permissions.
- Identity drift report widget (identity.admin): version, blueprint status,
service-account shape and self-minted tokens, break-glass, last identity
admin, qqq.io/* attribute validity, and service-account changes to
protected or superuser objects.
- Contract tests on Authentik 2026.8.3 (real session via the flow executor;
the drift report flags SuperuserFloorIT's lockout probes) and recorded
fixtures for the new unit tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: group permission mapper; harden phase 0/1 after code review
- AuthentikGroupPermissionMapper: token groups claim -> QQQ permissions via
the group graph and resolver; static fallbacks and bootstrapOperators;
Authentik unreachable grants only the fallbacks, never everything.
- Review fixes (general and silent-failure reviews):
- plain-text-only search, because Authentik's QLSearch could reach
sensitive fields
- thread-safe group graph
- a failed group-graph read shows "Unavailable", not empty permissions
- malformed qqq.io/* attributes are reported by the drift report
- operator filters may not widen the user scope; membership rows are
always scope-checked
- a missing pagination object is an error
- sorting by the key falls back to the default; strict booleans; ".."
keys refused
- https required except loopback / allowInsecureHttp; groupScopeNamePrefix
refused until implemented
- drift: unrunnable checks are ERROR "result unknown"; service-account
tokens filtered server-side, zero visible is a warning; deleted or
vanished targets flagged; only human users count as identity admins;
break-glass re-checked client-side
- redact error bodies before flattening; version check retried after a
failed fetch
- rehearse-phase0.sh: portable under Git Bash, waits for both blueprints,
derived is_superuser; passed on a synthetic production-like dump.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Set up from qbit-template-extension with the build used by
qbit-sql-workbench and qbit-quick-search: qbit-build-parent 2.0.0 with the
qqq 4.1.0-RC.2 BOM profile, Maven wrapper, Testcontainers ITs under
failsafe, JaCoCo gate, and the qqq-orb 0.6.8 CircleCI workflows (develop
snapshots, release/* RCs, v* releases). docs/DESIGN.md is the approved spec
(QRun-IO/qqq#1065). README, CLAUDE.md and AGENTS.md describe the layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>