Phase 5b of DESIGN.md section 12.4: the policy editor and saving policies to Authentik. It builds on #14.
What is in it
Edit Policy: a record action on authentik_group and on authentik_policy, never in the nav. It needs the POLICIES feature (which needs GROUPS) and authentik.policies.manage. It is a standard streamed-ETL action:
Edit step: shows the AUTHENTIK_POLICY_EDITOR widget. The Next UI component is a separate PR in qqq-frontend-next. The widget hands the generated document back as the process value policyDocument; API clients post that value directly.
Review (a dry run):
parses the document, failing closed
regenerates it in canonical form and checks the round trip
refuses new resources the app does not define, and keeps existing unknowns with a note
applies the subset rule to the permissions it adds
lists what is added and removed, and how many members are affected
Result: re-reads the group, merge-patches qqq.io/policy into its current attributes (the key is removed when the policy is empty), and writes the audit row and the Authentik event. Second-person approval is optional (policyTwoPersonApproval).
PolicyCatalog: the apps, tables, processes, reports and protected widgets a policy can grant, grouped by app, with the actions QQQ actually checks.
Table actions are limited by the table's capabilities and permission level.
Hidden record actions are included.
READ_WRITE tables offer read only, with a note.
Objects that are not protected, or have a custom checker or a dotted base name, are left out.
The catalog's permission names join the runtime permission catalog.
authentik_policy: a local table with one row per group, written only by Edit Policy. Field-level QQQ audit is the change history. It is in the nav under Access and appears as a child list on the group. The DDL is in docs/schema.sql.
Widget authentikPolicyEditor: sends the catalog and the group's live document, the source of truth. It flags a policy changed in Authentik since the last QQQ save, and an invalid stored document.
Tests
CI=true ./mvnw verify: 190 unit tests and 43 contract tests on Authentik 2026.8.3, all green; checkstyle and JaCoCo pass.
a save that writes the canonical document and the history row
no change, and removal
refusals: invalid document, other spec, unknown resource, subset rule, superuser group, stale fingerprint
kept unknowns, and replacing an invalid stored document
gating by permission and feature
two-person approval
the process end to end, from a group and from a policy record
widget data, including the changed-in-Authentik flag
QbitWriteContractIT.savePolicy: on a real Authentik, the subset refusal, the exact stored string, other attributes kept, the graph reading the policy back, and clearing it.
Phase 5b of DESIGN.md section 12.4: the policy editor and saving policies to Authentik. It builds on #14.
## What is in it
- **Edit Policy**: a record action on `authentik_group` and on `authentik_policy`, never in the nav. It needs the `POLICIES` feature (which needs `GROUPS`) and `authentik.policies.manage`. It is a standard streamed-ETL action:
- **Edit step**: shows the `AUTHENTIK_POLICY_EDITOR` widget. The Next UI component is a separate PR in qqq-frontend-next. The widget hands the generated document back as the process value `policyDocument`; API clients post that value directly.
- **Review** (a dry run):
- parses the document, failing closed
- regenerates it in canonical form and checks the round trip
- refuses new resources the app does not define, and keeps existing unknowns with a note
- applies the subset rule to the permissions it adds
- lists what is added and removed, and how many members are affected
- **Result**: re-reads the group, merge-patches `qqq.io/policy` into its current attributes (the key is removed when the policy is empty), and writes the audit row and the Authentik event. Second-person approval is optional (`policyTwoPersonApproval`).
- **`PolicyCatalog`**: the apps, tables, processes, reports and protected widgets a policy can grant, grouped by app, with the actions QQQ actually checks.
- Table actions are limited by the table's capabilities and permission level.
- Hidden record actions are included.
- `READ_WRITE` tables offer `read` only, with a note.
- Objects that are not protected, or have a custom checker or a dotted base name, are left out.
- The catalog's permission names join the runtime permission catalog.
- **`authentik_policy`**: a local table with one row per group, written only by Edit Policy. Field-level QQQ audit is the change history. It is in the nav under Access and appears as a child list on the group. The DDL is in `docs/schema.sql`.
- **Widget `authentikPolicyEditor`**: sends the catalog and the group's live document, the source of truth. It flags a policy changed in Authentik since the last QQQ save, and an invalid stored document.
## Tests
`CI=true ./mvnw verify`: 190 unit tests and 43 contract tests on Authentik 2026.8.3, all green; checkstyle and JaCoCo pass.
- `PolicyCatalogTest`: apps, capabilities, levels, shared base names, hidden actions, Other, exclusions.
- `PolicyEditorTest`:
- a save that writes the canonical document and the history row
- no change, and removal
- refusals: invalid document, other spec, unknown resource, subset rule, superuser group, stale fingerprint
- kept unknowns, and replacing an invalid stored document
- gating by permission and feature
- two-person approval
- the process end to end, from a group and from a policy record
- widget data, including the changed-in-Authentik flag
- `QbitWriteContractIT.savePolicy`: on a real Authentik, the subset refusal, the exact stored string, other attributes kept, the graph reading the policy back, and clearing it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Phase 5b of DESIGN.md section 12.4: the policy editor and saving policies to Authentik. It builds on #14.
What is in it
authentik_groupand onauthentik_policy, never in the nav. It needs thePOLICIESfeature (which needsGROUPS) andauthentik.policies.manage. It is a standard streamed-ETL action:AUTHENTIK_POLICY_EDITORwidget. The Next UI component is a separate PR in qqq-frontend-next. The widget hands the generated document back as the process valuepolicyDocument; API clients post that value directly.qqq.io/policyinto its current attributes (the key is removed when the policy is empty), and writes the audit row and the Authentik event. Second-person approval is optional (policyTwoPersonApproval).PolicyCatalog: the apps, tables, processes, reports and protected widgets a policy can grant, grouped by app, with the actions QQQ actually checks.READ_WRITEtables offerreadonly, with a note.authentik_policy: a local table with one row per group, written only by Edit Policy. Field-level QQQ audit is the change history. It is in the nav under Access and appears as a child list on the group. The DDL is indocs/schema.sql.authentikPolicyEditor: sends the catalog and the group's live document, the source of truth. It flags a policy changed in Authentik since the last QQQ save, and an invalid stored document.Tests
CI=true ./mvnw verify: 190 unit tests and 43 contract tests on Authentik 2026.8.3, all green; checkstyle and JaCoCo pass.PolicyCatalogTest: apps, capabilities, levels, shared base names, hidden actions, Other, exclusions.PolicyEditorTest:QbitWriteContractIT.savePolicy: on a real Authentik, the subset refusal, the exact stored string, other attributes kept, the graph reading the policy back, and clearing it.🤖 Generated with Claude Code