feat(webui): console authorization, RBAC, redaction, and audit model (Closes #633) #811

Merged
sysadmin merged 2 commits from feat/issue-633-console-authz-audit-model into master 2026-07-22 22:16:39 -05:00
Owner

Closes #633

Phase 1 of the Web Console epic #631. Its predecessor, the architecture ADR #632, landed as PR #796; this is the auth/audit foundation that the epic's phase ordering (AC3) requires before any dashboard child ships.

Problem

The read-only MVP (#426–#436) ships with no authentication. Protection comes from network placement alone, which #435 documents plainly. That is adequate while every route is a GET, and inadequate the moment Phase 2 wires a gated write.

#633 asks for the model those writes must pass through. This lands the authority before any write exists, so a write cannot later be added without something to check it against.

What this delivers

Concern Module
Identity, roles, authorization decision webui/console_authz.py
Secret redaction for every surface webui/console_redaction.py
Audit event schema, retention, sink webui/console_audit.py
Machine-readable publication GET /api/console/security-model
Written model docs/webui-authz-audit.md

Authorization (console_authz). Three identity sources (none default, local-dev, access-proxy), a four-role matrix (viewer, operator, controller, admin), the privileged-action list, and a fail-closed authorize(). Every console action carries a task_key into task_capability_map — the same source of truth gitea_resolve_task_capability and the MCP tool gates use — so the console cannot invent an authority the MCP layer does not already define. The role is always server-side configuration; a client cannot assert one. Deny reasons are closed and enumerated, there is no implicit allow branch, and even an allow reports execution_enabled: false while ACTIVE_PHASE is 1.

Redaction (console_redaction). One pass over API payloads, rendered HTML, logs, and audit records. It reuses gitea_audit.redact as the shared authority rather than forking it, then adds console patterns for keychain references, credential assignments, PEM private-key blocks, and JWTs. It never raises: an unredactable value degrades to the placeholder rather than being emitted raw.

Audit (console_audit). gitea_audit records MCP mutations; it has no console actor, identity source, correlation id, or retention class, and an authorization denial is not a mutation, so it would never appear there at all. The console record is additive, joined to the MCP record on correlation.request_id. Records are redacted at build time, re-scanned at write time, and dropped rather than persisted if they still trip a detector. Retention is per-record (90 / 365 / 730 days); an unknown action is retained as privileged rather than standard, because for a safety control the conservative direction is to keep the record longer.

Wiring (app.py). The existing preview and attempt routes gain an authorization block and emit a decision record. The terminal outcome is unchanged — webui/gated_actions.py still fails closed for every action — so this layer cannot loosen anything. Phase 2 enforces on this same decision instead of adding a parallel check.

Reviewer note: one deliberate gap, stated plainly

probe_auth_required() reads WEBUI_REQUIRE_PROBE_AUTH and no route consults it. Setting that variable changes nothing at runtime today.

I found this while reviewing the module against the issue's security requirement ("fail closed on missing auth for non-public health probes if configured") and chose to document it rather than wire it: enforcing it would change the MVP /health contract, which is outside this issue's accepted scope. The documentation now says it enforces nothing today, and test_probe_auth_is_declared_but_not_yet_enforced pins that status with a failure message pointing at the doc section to update. Wiring it in Phase 2 is therefore a deliberate change rather than a silent one.

The alternative — leaving the doc implying enforcement — would leave an operator who sets the variable believing a probe is protected worse off than one who knows it is not.

Provenance of the work

The worktree for this issue held roughly 1,900 lines of uncommitted, unpublished work from an earlier session whose lease had expired and whose owning process was dead. The control plane reported the issue unclaimed (gitea_assess_work_issue_duplicate: block: false, claim_status: not_claimed, no linked PR, no conflicting branch), and the allocator offered it as unallocated work.

I allocated it normally, adopted that WIP rather than discarding it, and reviewed it as a reviewer would. Two changes are mine:

  1. The redaction rule table in docs/webui-authz-audit.md embedded literal assignment-shaped examples, so the document tripped the very detectors it documents and its own scan_for_secrets test failed. Fixed in the document by naming the keys bare — the detectors were not weakened.
  2. The probe-auth gap above: documentation correction plus the pinning regression test.

Acceptance criteria

AC Status
1. Written RBAC matrix and privileged-action list docs/webui-authz-audit.md, rbac_matrix()
2. Redaction rules documented and unit-tested redaction_policy(), TestRedaction
3. Audit event schema with required fields and retention defaults audit_policy(), REQUIRED_FIELDS, 90/365/730
4. Integration points defined for the Phase 2 action framework preview/attempt blocks, security-model endpoint
5. Local-dev mode documented with explicit insecurity warnings "Local-dev mode" section, marked INSECURE

Required tests named by the issue — redaction units (token, keychain, password), default-deny for unauthenticated write stubs, and audit record creation for a simulated privileged preview — are all present.

Verification

Run with venv/bin/python -m pytest from the allocated worktree.

  • New suite tests/test_webui_console_authz_audit.py75 passed, 93 subtests.
  • Affected suites (-k "webui or gated or audit or redact") — 403 passed, 237 subtests.
  • Full suite — 4392 passed, 11 failed, 6 skipped, 624 subtests.

Baseline: a clean detached worktree pinned to master 9eb0f29cefa85fd0ecbbb5f923ebc748bffa81f8 reproduces the same eleven failing node identifiers — six in test_commit_payloads.py, two in test_issue_702_review_findings_f1_f6.py, and one each in test_mcp_server.py::TestPreflightVerification, test_post_merge_moot_lease.py, and test_reconciler_supersession_close.py. None touch console, redaction, or audit code. This branch adds no new failures.

I also verified the acceptance criteria directly rather than only through the inherited tests: every action resolves to a real MCP permission, anonymous is denied on all nine actions, unknown action and unknown role deny by their own reason codes, an admin allow still reports execution_enabled: false, requesting execution returns phase_not_active, sample credentials redact, and a built record carries every required field with no detector hit.

Scope

Seven files, 2069 insertions, 1 deletion. No production secrets. No write is enabled by this change, and ACTIVE_PHASE stays 1.

Provenance

  • Issue: #633 · Parent epic: #631 · Extends: #435, Web UI: Gated action framework (#434)
  • Branch: feat/issue-633-console-authz-audit-model
  • Worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-633-console-authz-audit-model
  • Commit: 479e434f921e556fb7a64cf52194db1a69ff8454
  • Base: master at 9eb0f29cefa85fd0ecbbb5f923ebc748bffa81f8
Closes #633 Phase 1 of the Web Console epic #631. Its predecessor, the architecture ADR #632, landed as PR #796; this is the auth/audit foundation that the epic's phase ordering (AC3) requires before any dashboard child ships. ## Problem The read-only MVP (#426–#436) ships with no authentication. Protection comes from network placement alone, which #435 documents plainly. That is adequate while every route is a GET, and inadequate the moment Phase 2 wires a gated write. #633 asks for the model those writes must pass through. This lands the authority **before** any write exists, so a write cannot later be added without something to check it against. ## What this delivers | Concern | Module | |---|---| | Identity, roles, authorization decision | `webui/console_authz.py` | | Secret redaction for every surface | `webui/console_redaction.py` | | Audit event schema, retention, sink | `webui/console_audit.py` | | Machine-readable publication | `GET /api/console/security-model` | | Written model | `docs/webui-authz-audit.md` | **Authorization (`console_authz`).** Three identity sources (`none` default, `local-dev`, `access-proxy`), a four-role matrix (viewer, operator, controller, admin), the privileged-action list, and a fail-closed `authorize()`. Every console action carries a `task_key` into `task_capability_map` — the same source of truth `gitea_resolve_task_capability` and the MCP tool gates use — so the console cannot invent an authority the MCP layer does not already define. The role is always server-side configuration; a client cannot assert one. Deny reasons are closed and enumerated, there is no implicit allow branch, and even an allow reports `execution_enabled: false` while `ACTIVE_PHASE` is 1. **Redaction (`console_redaction`).** One pass over API payloads, rendered HTML, logs, and audit records. It reuses `gitea_audit.redact` as the shared authority rather than forking it, then adds console patterns for keychain references, credential assignments, PEM private-key blocks, and JWTs. It never raises: an unredactable value degrades to the placeholder rather than being emitted raw. **Audit (`console_audit`).** `gitea_audit` records MCP *mutations*; it has no console actor, identity source, correlation id, or retention class, and an authorization **denial** is not a mutation, so it would never appear there at all. The console record is additive, joined to the MCP record on `correlation.request_id`. Records are redacted at build time, re-scanned at write time, and dropped rather than persisted if they still trip a detector. Retention is per-record (90 / 365 / 730 days); an unknown action is retained as privileged rather than standard, because for a safety control the conservative direction is to keep the record longer. **Wiring (`app.py`).** The existing preview and attempt routes gain an `authorization` block and emit a decision record. The terminal outcome is unchanged — `webui/gated_actions.py` still fails closed for every action — so this layer cannot loosen anything. Phase 2 enforces on this same decision instead of adding a parallel check. ## Reviewer note: one deliberate gap, stated plainly `probe_auth_required()` reads `WEBUI_REQUIRE_PROBE_AUTH` and **no route consults it**. Setting that variable changes nothing at runtime today. I found this while reviewing the module against the issue's security requirement ("fail closed on missing auth for non-public health probes *if configured*") and chose to document it rather than wire it: enforcing it would change the MVP `/health` contract, which is outside this issue's accepted scope. The documentation now says it enforces nothing today, and `test_probe_auth_is_declared_but_not_yet_enforced` pins that status with a failure message pointing at the doc section to update. Wiring it in Phase 2 is therefore a deliberate change rather than a silent one. The alternative — leaving the doc implying enforcement — would leave an operator who sets the variable believing a probe is protected worse off than one who knows it is not. ## Provenance of the work The worktree for this issue held roughly 1,900 lines of uncommitted, unpublished work from an earlier session whose lease had expired and whose owning process was dead. The control plane reported the issue unclaimed (`gitea_assess_work_issue_duplicate`: `block: false`, `claim_status: not_claimed`, no linked PR, no conflicting branch), and the allocator offered it as unallocated work. I allocated it normally, adopted that WIP rather than discarding it, and reviewed it as a reviewer would. Two changes are mine: 1. The redaction rule table in `docs/webui-authz-audit.md` embedded literal assignment-shaped examples, so the document tripped the very detectors it documents and its own `scan_for_secrets` test failed. Fixed in the document by naming the keys bare — the detectors were not weakened. 2. The probe-auth gap above: documentation correction plus the pinning regression test. ## Acceptance criteria | AC | Status | |---|---| | 1. Written RBAC matrix and privileged-action list | `docs/webui-authz-audit.md`, `rbac_matrix()` | | 2. Redaction rules documented and unit-tested | `redaction_policy()`, `TestRedaction` | | 3. Audit event schema with required fields and retention defaults | `audit_policy()`, `REQUIRED_FIELDS`, 90/365/730 | | 4. Integration points defined for the Phase 2 action framework | preview/attempt blocks, `security-model` endpoint | | 5. Local-dev mode documented with explicit insecurity warnings | "Local-dev mode" section, marked INSECURE | Required tests named by the issue — redaction units (token, keychain, password), default-deny for unauthenticated write stubs, and audit record creation for a simulated privileged preview — are all present. ## Verification Run with `venv/bin/python -m pytest` from the allocated worktree. - New suite `tests/test_webui_console_authz_audit.py` — **75 passed, 93 subtests**. - Affected suites (`-k "webui or gated or audit or redact"`) — **403 passed, 237 subtests**. - Full suite — **4392 passed, 11 failed, 6 skipped, 624 subtests**. Baseline: a clean detached worktree pinned to master `9eb0f29cefa85fd0ecbbb5f923ebc748bffa81f8` reproduces the **same eleven** failing node identifiers — six in `test_commit_payloads.py`, two in `test_issue_702_review_findings_f1_f6.py`, and one each in `test_mcp_server.py::TestPreflightVerification`, `test_post_merge_moot_lease.py`, and `test_reconciler_supersession_close.py`. None touch console, redaction, or audit code. This branch adds no new failures. I also verified the acceptance criteria directly rather than only through the inherited tests: every action resolves to a real MCP permission, anonymous is denied on all nine actions, unknown action and unknown role deny by their own reason codes, an admin allow still reports `execution_enabled: false`, requesting execution returns `phase_not_active`, sample credentials redact, and a built record carries every required field with no detector hit. ## Scope Seven files, 2069 insertions, 1 deletion. No production secrets. No write is enabled by this change, and `ACTIVE_PHASE` stays 1. ## Provenance - Issue: #633 · Parent epic: #631 · Extends: #435, #434 - Branch: `feat/issue-633-console-authz-audit-model` - Worktree: `/Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-633-console-authz-audit-model` - Commit: `479e434f921e556fb7a64cf52194db1a69ff8454` - Base: master at `9eb0f29cefa85fd0ecbbb5f923ebc748bffa81f8`
jcwalker3 added 1 commit 2026-07-22 14:41:33 -05:00
Phase 1 of the MCP Control Plane Web Console (#631) defines the model that
future gated writes must pass through, and enables none of them.

The read-only MVP (#426-#436) ships with no authentication; protection comes
from network placement alone (#435). That is adequate while every route is a
GET and inadequate the moment Phase 2 wires a write. This lands the authority
first, so no write can later be added without something to check it against.

webui/console_authz.py
  Identity sources (none / local-dev / access-proxy), a four-role matrix
  (viewer, operator, controller, admin), the privileged-action list, and a
  fail-closed authorize(). Every action maps to a task_key in
  task_capability_map, so the console cannot invent an authority the MCP layer
  does not already define. Roles are always server-side configuration, never a
  client assertion. Deny reasons are closed and enumerated; there is no
  implicit allow branch, and even an allow reports execution_enabled=false
  while ACTIVE_PHASE is 1.

webui/console_redaction.py
  One redaction pass for API payloads, rendered HTML, logs, and audit records.
  Reuses gitea_audit.redact as the shared authority rather than forking it,
  then adds console patterns for keychain references, credential assignments,
  PEM private-key blocks, and JWTs. Never raises: an unredactable value
  degrades to the placeholder rather than being emitted raw.

webui/console_audit.py
  Console-side audit records, which gitea_audit cannot supply: it records MCP
  mutations and carries no console actor, identity source, correlation id, or
  retention class, and an authorization denial is not a mutation at all. The
  two are additive and join on correlation.request_id. Records are redacted at
  build time, re-scanned at write time, and dropped rather than persisted if
  they still trip a detector. Retention is per-record; an unknown action is
  retained as privileged rather than standard.

webui/app.py
  Attaches an authorization block to the existing preview and attempt routes
  and records the decision. The terminal outcome is unchanged - gated_actions
  still fails closed for every action - so this cannot loosen anything. Adds
  GET /api/console/security-model publishing the three policies as JSON.

Probe authentication is deliberately declarative in this slice:
probe_auth_required() reports operator intent and no route consults it. The
documentation says so plainly and a regression test pins the not-enforced
status, so wiring it in Phase 2 is a deliberate change rather than a silent
one. An operator who sets the variable believing it protects a probe would be
worse off than one who knows it does not.

Tests: tests/test_webui_console_authz_audit.py - 75 passed, 93 subtests,
covering each acceptance criterion and each test the issue requires
(redaction units, default-deny for unauthenticated write stubs, audit record
creation for a simulated privileged preview).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #811
issue: #633
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 97038-2788fbc2067c
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-811-479e434
phase: claimed
candidate_head: 479e434f92
target_branch: master
target_branch_sha: none
last_activity: 2026-07-23T01:24:29Z
expires_at: 2026-07-23T01:34:29Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #811 issue: #633 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 97038-2788fbc2067c worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-811-479e434 phase: claimed candidate_head: 479e434f921e556fb7a64cf52194db1a69ff8454 target_branch: master target_branch_sha: none last_activity: 2026-07-23T01:24:29Z expires_at: 2026-07-23T01:34:29Z blocker: none
sysadmin approved these changes 2026-07-22 20:30:39 -05:00
Dismissed
sysadmin left a comment
Owner

Review — PR #811 at 479e434f92

Independent reviewer validation against Issue #633 (authoritative). Reviewer: sysadmin / prgs-reviewer; author: jcwalker3 — independence confirmed.

Acceptance criteria → evidence

  1. Written RBAC matrix and privileged-action listdocs/webui-authz-audit.md plus webui/console_authz.py::rbac_matrix(): four ordered roles (viewer/operator/controller/admin), nine actions each bound to a real task_capability_map task key; merge/delete flagged dual-control + break-glass. Verified each task_key resolves to a real MCP permission in task_capability_map.py.
  2. Redaction rules documented and unit-testedwebui/console_redaction.py reuses gitea_audit.redact (no fork) and adds console patterns (keychain refs/commands, credential assignments, PEM blocks, JWTs, bearer). Fail-closed (never raises; unredactable → placeholder). TestRedaction covers token, keychain, password patterns as the issue requires.
  3. Audit event schema with required fields and retention defaultswebui/console_audit.py: all required fields asserted by tests; retention 90/365/730 days; unknown action conservatively retained as privileged; redact-at-build plus re-scan-and-drop at write; append-only JSONL, off by default.
  4. Phase 2 integration points — preview/attempt routes attach an authorization decision block and emit audit events; GET /api/console/security-model publishes the model; response-shape change is additive only.
  5. Local-dev mode with explicit insecurity warnings — documented INSECURE, loopback-only; role is always server-side config, never client-asserted (tested).

Security review

Default-deny verified: anonymous denied on all nine actions; unknown action/role deny with closed reason codes; even an allow reports execution_enabled: false while ACTIVE_PHASE=1; execution requests deny phase_not_active. webui/gated_actions.py still fails closed (_MVP_ACTIONS_ENABLED = False), so this PR enables no write. No secrets, tokens, or credentials in the diff; security-model endpoint scan returns no detector hits.

The one declared gap — WEBUI_REQUIRE_PROBE_AUTH is declared but not yet enforced by any route — is documented plainly in docs/webui-authz-audit.md and pinned by test_probe_auth_is_declared_but_not_yet_enforced, which fails with a pointer to the doc if it is ever wired. The MVP has only the public /health probe, so there is no non-public probe left unprotected today. Accepted as a deliberate, documented Phase 2 handoff, not a silent hole.

Independent validation (reviewer worktree, exact head 479e434)

  • tests/test_webui_console_authz_audit.py: 75 passed, 93 subtests.
  • Affected suites -k "webui or gated or audit or redact": 404 passed, 237 subtests.
  • Full suite: 4393 passed, 11 failed, 6 skipped, 624 subtests — the same 11 node IDs reproduce identically on a clean detached worktree at master 53c2c92782fdb4b3ac4af922e6814f345141f82f (6× test_commit_payloads, 2× test_issue_702, 1 each test_mcp_server/TestPreflightVerification, test_post_merge_moot_lease, test_reconciler_supersession_close). Pre-existing, unrelated to console/authz/audit code; this PR introduces no new failures.
  • Reviewer worktree and index clean after test runs; no tracked-file changes produced.

Scope

7 files, +2069/−1: three new webui modules, one additive app.py wiring block, two docs, one new test module. No unrelated or speculative changes.

All Issue #633 acceptance criteria and required tests are satisfied. No blocking findings.

## Review — PR #811 at 479e434f921e556fb7a64cf52194db1a69ff8454 Independent reviewer validation against Issue #633 (authoritative). Reviewer: sysadmin / prgs-reviewer; author: jcwalker3 — independence confirmed. ### Acceptance criteria → evidence 1. **Written RBAC matrix and privileged-action list** — `docs/webui-authz-audit.md` plus `webui/console_authz.py::rbac_matrix()`: four ordered roles (viewer/operator/controller/admin), nine actions each bound to a real `task_capability_map` task key; merge/delete flagged dual-control + break-glass. Verified each `task_key` resolves to a real MCP permission in `task_capability_map.py`. 2. **Redaction rules documented and unit-tested** — `webui/console_redaction.py` reuses `gitea_audit.redact` (no fork) and adds console patterns (keychain refs/commands, credential assignments, PEM blocks, JWTs, bearer). Fail-closed (never raises; unredactable → placeholder). `TestRedaction` covers token, keychain, password patterns as the issue requires. 3. **Audit event schema with required fields and retention defaults** — `webui/console_audit.py`: all required fields asserted by tests; retention 90/365/730 days; unknown action conservatively retained as privileged; redact-at-build plus re-scan-and-drop at write; append-only JSONL, off by default. 4. **Phase 2 integration points** — preview/attempt routes attach an `authorization` decision block and emit audit events; `GET /api/console/security-model` publishes the model; response-shape change is additive only. 5. **Local-dev mode with explicit insecurity warnings** — documented INSECURE, loopback-only; role is always server-side config, never client-asserted (tested). ### Security review Default-deny verified: anonymous denied on all nine actions; unknown action/role deny with closed reason codes; even an allow reports `execution_enabled: false` while `ACTIVE_PHASE=1`; execution requests deny `phase_not_active`. `webui/gated_actions.py` still fails closed (`_MVP_ACTIONS_ENABLED = False`), so this PR enables no write. No secrets, tokens, or credentials in the diff; security-model endpoint scan returns no detector hits. The one declared gap — `WEBUI_REQUIRE_PROBE_AUTH` is declared but not yet enforced by any route — is documented plainly in `docs/webui-authz-audit.md` and pinned by `test_probe_auth_is_declared_but_not_yet_enforced`, which fails with a pointer to the doc if it is ever wired. The MVP has only the public `/health` probe, so there is no non-public probe left unprotected today. Accepted as a deliberate, documented Phase 2 handoff, not a silent hole. ### Independent validation (reviewer worktree, exact head 479e434) - `tests/test_webui_console_authz_audit.py`: **75 passed, 93 subtests**. - Affected suites `-k "webui or gated or audit or redact"`: **404 passed, 237 subtests**. - Full suite: **4393 passed, 11 failed, 6 skipped, 624 subtests** — the same 11 node IDs reproduce identically on a clean detached worktree at master `53c2c92782fdb4b3ac4af922e6814f345141f82f` (6× test_commit_payloads, 2× test_issue_702, 1 each test_mcp_server/TestPreflightVerification, test_post_merge_moot_lease, test_reconciler_supersession_close). Pre-existing, unrelated to console/authz/audit code; this PR introduces no new failures. - Reviewer worktree and index clean after test runs; no tracked-file changes produced. ### Scope 7 files, +2069/−1: three new webui modules, one additive app.py wiring block, two docs, one new test module. No unrelated or speculative changes. All Issue #633 acceptance criteria and required tests are satisfied. No blocking findings.
jcwalker3 added 1 commit 2026-07-22 21:57:59 -05:00
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #811
issue: #633
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 38446-9c698fc1363c
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr811-66a89a46
phase: claimed
candidate_head: 66a89a46bb
target_branch: master
target_branch_sha: 53c2c92782
last_activity: 2026-07-23T03:02:16Z
expires_at: 2026-07-23T03:12:16Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #811 issue: #633 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 38446-9c698fc1363c worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr811-66a89a46 phase: claimed candidate_head: 66a89a46bba01c194f20ea7f5858da8781f3e9cf target_branch: master target_branch_sha: 53c2c92782fdb4b3ac4af922e6814f345141f82f last_activity: 2026-07-23T03:02:16Z expires_at: 2026-07-23T03:12:16Z blocker: none
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #811
issue: #633
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 38446-9c698fc1363c
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr811-66a89a46
phase: validated
candidate_head: 66a89a46bb
target_branch: master
target_branch_sha: 53c2c92782
last_activity: 2026-07-23T03:07:15Z
expires_at: 2026-07-23T03:17:15Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #811 issue: #633 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 38446-9c698fc1363c worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr811-66a89a46 phase: validated candidate_head: 66a89a46bba01c194f20ea7f5858da8781f3e9cf target_branch: master target_branch_sha: 53c2c92782fdb4b3ac4af922e6814f345141f82f last_activity: 2026-07-23T03:07:15Z expires_at: 2026-07-23T03:17:15Z blocker: none
sysadmin approved these changes 2026-07-22 22:09:12 -05:00
sysadmin left a comment
Owner

Canonical PR State

STATE: approved
WHO_IS_NEXT: merger
NEXT_ACTION: Merge PR #811 at the approved head only after merger preflight and sync assessment recommend merge_now.
NEXT_PROMPT:

Merge PR #811 for Scaled-Tech-Consulting/Gitea-Tools using gitea-merger / prgs-merger only. Approved head: 66a89a46bba01c194f20ea7f5858da8781f3e9cf. Linked issue #633. Do not re-review unless head moved. Run full merger workflow: whoami, capability, adopt/acquire lease, assess_pr_sync_status, merge only if merge_now and approval_at_current_head.

WHAT_HAPPENED: Fresh independent reviewer approval at synchronized head 66a89a46 (supersedes stale Review #509 at 479e434).
WHY: All Issue #633 acceptance criteria and required tests pass on the complete resulting change after master sync; no blocking findings.
ISSUE: #633
HEAD_SHA: 66a89a46bb
REVIEW_STATUS: approved / approval_at_current_head
MERGE_READY: true
BLOCKERS: none
VALIDATION: Focused tests/test_webui_console_authz_audit.py 75 passed 93 subtests; broader -k webui|gated|audit|redact 426 passed; full suite 4472 passed 11 failed identical to master baseline 53c2c927; worktree clean; sync base 53c2c927 is parent of merge commit 66a89a46.
NATIVE_REVIEW_PROOF: transport=native_mcp; entrypoint=mcp_server; token_fingerprint=5dff9a62607f4833
LAST_UPDATED_BY: sysadmin / prgs-reviewer / 2026-07-23


Review body — PR #811 at 66a89a46bb

Fresh independent re-review after branch synchronization. Do not reuse Review #509 (old head 479e434). Reviewer: sysadmin / prgs-reviewer; author: jcwalker3 — independence confirmed.

Synchronization proof

  • Live head: 66a89a46bb
  • Merge parents: 479e434 (feature) + 53c2c92782 (master)
  • Base is ancestor of PR head; not already landed
  • Complete change vs master: 7 files, +2069/-1

Acceptance criteria (Issue #633)

  1. RBAC matrix + privileged-action list — docs + console_authz.rbac_matrix(); nine actions map to real task_capability_map permissions
  2. Redaction rules documented and unit-tested — console_redaction reuses gitea_audit.redact; TestRedaction covers token/keychain/password
  3. Audit schema + retention 90/365/730 — console_audit REQUIRED_FIELDS; fail-closed write path
  4. Phase 2 integration — preview/attempt authorization blocks + /api/console/security-model; writes still disabled
  5. Local-dev INSECURE warnings documented and tested

Security

Default-deny for anonymous; execution_enabled false while ACTIVE_PHASE=1; gated_actions still fail closed. Declared probe-auth non-enforcement documented and pinned. No production secrets.

Validation (branches/review-pr811-66a89a46)

  • 75 focused / 426 broader / 4472 full-suite passed
  • Same 11 baseline failures on master 53c2c927
  • Worktree clean before/after

All gates pass. APPROVE.

## Canonical PR State STATE: approved WHO_IS_NEXT: merger NEXT_ACTION: Merge PR #811 at the approved head only after merger preflight and sync assessment recommend merge_now. NEXT_PROMPT: ```text Merge PR #811 for Scaled-Tech-Consulting/Gitea-Tools using gitea-merger / prgs-merger only. Approved head: 66a89a46bba01c194f20ea7f5858da8781f3e9cf. Linked issue #633. Do not re-review unless head moved. Run full merger workflow: whoami, capability, adopt/acquire lease, assess_pr_sync_status, merge only if merge_now and approval_at_current_head. ``` WHAT_HAPPENED: Fresh independent reviewer approval at synchronized head 66a89a46 (supersedes stale Review #509 at 479e434). WHY: All Issue #633 acceptance criteria and required tests pass on the complete resulting change after master sync; no blocking findings. ISSUE: #633 HEAD_SHA: 66a89a46bba01c194f20ea7f5858da8781f3e9cf REVIEW_STATUS: approved / approval_at_current_head MERGE_READY: true BLOCKERS: none VALIDATION: Focused tests/test_webui_console_authz_audit.py 75 passed 93 subtests; broader -k webui|gated|audit|redact 426 passed; full suite 4472 passed 11 failed identical to master baseline 53c2c927; worktree clean; sync base 53c2c927 is parent of merge commit 66a89a46. NATIVE_REVIEW_PROOF: transport=native_mcp; entrypoint=mcp_server; token_fingerprint=5dff9a62607f4833 LAST_UPDATED_BY: sysadmin / prgs-reviewer / 2026-07-23 --- ## Review body — PR #811 at 66a89a46bba01c194f20ea7f5858da8781f3e9cf Fresh independent re-review after branch synchronization. Do not reuse Review #509 (old head 479e434). Reviewer: sysadmin / prgs-reviewer; author: jcwalker3 — independence confirmed. ### Synchronization proof - Live head: 66a89a46bba01c194f20ea7f5858da8781f3e9cf - Merge parents: 479e434 (feature) + 53c2c92782fdb4b3ac4af922e6814f345141f82f (master) - Base is ancestor of PR head; not already landed - Complete change vs master: 7 files, +2069/-1 ### Acceptance criteria (Issue #633) 1. RBAC matrix + privileged-action list — docs + console_authz.rbac_matrix(); nine actions map to real task_capability_map permissions 2. Redaction rules documented and unit-tested — console_redaction reuses gitea_audit.redact; TestRedaction covers token/keychain/password 3. Audit schema + retention 90/365/730 — console_audit REQUIRED_FIELDS; fail-closed write path 4. Phase 2 integration — preview/attempt authorization blocks + /api/console/security-model; writes still disabled 5. Local-dev INSECURE warnings documented and tested ### Security Default-deny for anonymous; execution_enabled false while ACTIVE_PHASE=1; gated_actions still fail closed. Declared probe-auth non-enforcement documented and pinned. No production secrets. ### Validation (branches/review-pr811-66a89a46) - 75 focused / 426 broader / 4472 full-suite passed - Same 11 baseline failures on master 53c2c927 - Worktree clean before/after All gates pass. APPROVE.
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #811
issue: #633
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 38446-9c698fc1363c
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr811-66a89a46
phase: released
candidate_head: 66a89a46bb
target_branch: master
target_branch_sha: 53c2c92782
last_activity: 2026-07-23T03:09:26Z
expires_at: 2026-07-23T03:19:26Z
blocker: manual-release

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #811 issue: #633 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 38446-9c698fc1363c worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr811-66a89a46 phase: released candidate_head: 66a89a46bba01c194f20ea7f5858da8781f3e9cf target_branch: master target_branch_sha: 53c2c92782fdb4b3ac4af922e6814f345141f82f last_activity: 2026-07-23T03:09:26Z expires_at: 2026-07-23T03:19:26Z blocker: manual-release
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #811
issue: #633
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 71175-6a8151d431c5
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr811-66a89a46
phase: claimed
candidate_head: 66a89a46bb
target_branch: master
target_branch_sha: 53c2c92782
last_activity: 2026-07-23T03:16:27Z
expires_at: 2026-07-23T03:26:27Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #811 issue: #633 reviewer_identity: sysadmin profile: prgs-merger session_id: 71175-6a8151d431c5 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr811-66a89a46 phase: claimed candidate_head: 66a89a46bba01c194f20ea7f5858da8781f3e9cf target_branch: master target_branch_sha: 53c2c92782fdb4b3ac4af922e6814f345141f82f last_activity: 2026-07-23T03:16:27Z expires_at: 2026-07-23T03:26:27Z blocker: none
sysadmin merged commit 689c60fc7c into master 2026-07-22 22:16:39 -05:00
Owner

Stale #332 review-decision lock cleanup (#594)

Status: APPLIED

Manual deletion of session-state files is not the workflow.
This path only clears a lock when the referenced PR is merged/closed.

## Stale #332 review-decision lock cleanup (#594) Status: **APPLIED** - actor: `sysadmin` - profile: `prgs-merger` - timestamp: `2026-07-23T03:16:40.596718+00:00` - last terminal: `approve` on PR #811 - PR state: `closed` (merged=True) - merge_commit_sha: `689c60fc7c386dd25926f9161d93cfc25524c5d3` - prior live_mutations_count: `2` - prior profile_identity: `prgs-reviewer` Manual deletion of session-state files is **not** the workflow. This path only clears a lock when the referenced PR is merged/closed.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#811