neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
Plan: audit entries kept forever unless config sets retention_days
88a5fab · Gabriel Morell · 2026-10-20 21:12
Files changed
modified
docs/plans/org-audit-log.md
+20
−11
@@ -1,6 +1,6 @@
| 1 | 1 | # Plan: an audit log for organizations, repositories and the instance |
| 2 | 2 | |
| 3 | −Status: **draft — decisions recorded below (2026-10-20); retention still to confirm.** | |
| 3 | +Status: **ready to execute** — decisions recorded below (2026-10-20); the remaining open questions have recommended defaults. | |
| 4 | 4 | Independent of the CI plans. Supersedes the log lines added in 77def2d |
| 5 | 5 | ("Secrets: changing a secret or variable leaves a line in the log") and |
| 6 | 6 | closes item 12 of `security-review-ci.md` ("Missing audit logging for |
@@ -46,7 +46,7 @@ made this repo public / registered this runner?" Today:
| 46 | 46 | | Scope | `lib/git_gud/accounts/scope.ex`, built in `UserAuth.fetch_current_scope_for_user/2` and `mount_current_scope/2` | |
| 47 | 47 | | Settings nav | `org_settings_header/1`, `repo_settings_header/1` in `lib/git_gud_web/components/core_components.ex`; org settings links in `lib/git_gud_web/live/org_live/show.ex` | |
| 48 | 48 | | Router | org and repo settings pages live in `live_session :authenticated_repo` | |
| 49 | −| Retention precedent | `GitGud.Federation.Workers.Retention`; cron in `config/config.exs`; admin-editable knobs in `GitGud.Settings` (`@keys`) | | |
| 49 | +| Retention precedent | `GitGud.Federation.Workers.Retention`; cron in `config/config.exs` | | |
| 50 | 50 | | Pagination precedent | `OrgLive.Index` (`/orgs/page/:page`, `page_window/2`) | |
| 51 | 51 | | Mobile API auth | `GitGudWeb.Api.Mobile.Auth.require_scope/2`; scopes in `PersonalAccessToken` `@scopes` | |
| 52 | 52 | | CLI | `cli/src/commands/*.rs`, speaks `/api/mobile/v1` (`cli/README.md`: parity with Android) | |
@@ -347,10 +347,19 @@ Every view needs sudo mode; IP and user agent show only in the admin view.
| 347 | 347 | |
| 348 | 348 | ## 5. Retention |
| 349 | 349 | |
| 350 | +Kept forever by default. The window is application config, set in | |
| 351 | +`config/config.exs` (or a deployment's own config): | |
| 352 | + | |
| 353 | +```elixir | |
| 354 | +# nil keeps every entry; an integer N prunes entries older than N days. | |
| 355 | +config :git_gud, GitGud.Audit, retention_days: nil | |
| 356 | +``` | |
| 357 | + | |
| 350 | 358 | `GitGud.Audit.Workers.Prune` (`queue: :default`), `@daily` in the |
| 351 | −`config/config.exs` crontab. The window is a `GitGud.Settings` key | |
| 352 | −`audit.retention_days` (`type: :integer, default: nil, min: 30, nullable: true`), | |
| 353 | −editable by an admin without a deploy. `nil` keeps everything. Deletes in | |
| 359 | +`config/config.exs` crontab, reads it at run time and does nothing while | |
| 360 | +it is `nil`. Boot refuses a value that isn't `nil` or a positive integer. | |
| 361 | +Not an admin-editable `GitGud.Settings` key: shortening the audit trail | |
| 362 | +shouldn't be something a compromised admin session can do. Deletes in | |
| 354 | 363 | batches of 5 000 by id |
| 355 | 364 | (`DELETE … WHERE id IN (SELECT id … WHERE inserted_at < $1 LIMIT 5000)`) |
| 356 | 365 | until a batch comes back short, so no long lock, and records one |
@@ -418,7 +427,7 @@ Same table: `organization_id`/`repository_id` nil, `subject_user_id` and
| 418 | 427 | repo's entries render with the snapshot label. |
| 419 | 428 | - `audit_live/repo_test.exs`, `audit_live/admin_test.exs`: the same |
| 420 | 429 | permission matrix. |
| 421 | −- `test/git_gud/audit/workers/prune_test.exs`: nil keeps everything; N days | |
| 430 | +- `test/git_gud/audit/workers/prune_test.exs`: `retention_days: nil` keeps everything; N days | |
| 422 | 431 | deletes only older rows, across more than one batch; writes `audit.pruned`. |
| 423 | 432 | - API: without `audit:read` → 403; non-admin → 403; invisible org → 404; |
| 424 | 433 | `next_before`; no `ip` in the payload. |
@@ -427,8 +436,8 @@ Same table: `organization_id`/`repository_id` nil, `subject_user_id` and
| 427 | 436 | |
| 428 | 437 | 1. **Substrate.** Migration, `Audit.Entry`, `Audit.Catalog`, `Audit` |
| 429 | 438 | (`transact`, `insert!`, `multi`, `list`), `Audit.Actor`, Scope |
| 430 | − `ip`/`user_agent`, `:user_agent` connect_info, prune worker and | |
| 431 | − `audit.retention_days`. No callers, no UI. | |
| 439 | + `ip`/`user_agent`, `:user_agent` connect_info, prune worker and the | |
| 440 | + `retention_days` config. No callers, no UI. | |
| 432 | 441 | 2. **Secrets and the org page.** Both `SecretsLive` modules (drop `audit/4`; |
| 433 | 442 | `Audit`'s log line replaces it), `AuditLive.Org` with category tabs and |
| 434 | 443 | keyset paging, the nav entry, `AuditComponents`. Closes review item #12. |
@@ -460,9 +469,9 @@ Same table: `organization_id`/`repository_id` nil, `subject_user_id` and
| 460 | 469 | |
| 461 | 470 | - **Log line per entry: yes.** `Audit` keeps one `Logger.notice` per row |
| 462 | 471 | after commit (open question 1). |
| 463 | −- **Retention:** answered "yes" to "forever or 365 days" — to confirm | |
| 464 | − which. Until then slice 1 ships the `audit.retention_days` setting with | |
| 465 | − `nil` (keep everything) as the default, so nothing is pruned on a guess. | |
| 472 | +- **Retention: forever by default,** configurable to N days with | |
| 473 | + `config :git_gud, GitGud.Audit, retention_days: N` in `config.exs` | |
| 474 | + (section 5; open question 2). | |
| 466 | 475 | - **IP and user agent: instance admins only.** Org and repo views, and the |
| 467 | 476 | API, never show them; `/admin/audit` does (open question 3). |
| 468 | 477 | - **Viewing the log requires sudo mode.** All three views mount with |
Parents: 36965b5