## Why
The privilege cache could keep a grant authz-service had revoked:
- **Unordered keys:** each routing key has its own transient queue, so a late `Privilege.Added`/`User.Added` resurrected a revoked grant.
- **Startup gap:** services fetched `/authz` before binding their queues, so revocations published in between were lost until restart.
Design: ADR-0015 (docs PR, Proposed).
## What
- `Process` orders events by authz-service's global `sequenceNo` per (email, company). All four events come from the Company aggregate, so seq order equals commit order. An event only overrides older facts, and `User.Removed` stamps every privilege.
- Events without a sequence number fail closed: additions are dropped, and removals hold until the next snapshot. Negative or huge sequence numbers are dropped.
- `Fetch` checks the status and retries 503 (60×1 s, 30 s HTTP timeout). It reads `X-Authz-Sequence`, merges the snapshot as facts at that position, and raises a floor; snapshots older than the floor are ignored. A missing header merges at 0 with a warning (rollout window only).
- `CompaniesByUser` returns `[]`, and unknown privileges create no state. CLAUDE.md is rewritten.
**BREAKING:** `Process` without `SequenceNo` no longer grants, so service tests must set it. Services must call `Fetch()` after `conn.Start`.
## Verification
- `go test -race`: 98.3% coverage, including table-driven reorderings, snapshot-merge cases and a revocation-during-Fetch race test.
- 26 mutants on the ordering, merge and retry checks: all killed (each compiled and produced `--- FAIL`).
- prek passes.
**Expert review:** two rounds. Round 1: Security, Go Backend, Event Sourcing and Database experts reviewed both diffs. Round 2: Security and Event Sourcing re-reviewed the fixes. A final Event Sourcing review covered the committed-events wrapper. Fixed from the reviews: older snapshots merged after newer ones (floor check); a lagging read view serving snapshots that miss revocations (503 plus catch-up); a reset's TRUNCATE emptying a REPEATABLE READ snapshot (LOCK TABLE privileges, verified on PostgreSQL); seq-0 removals undone by older additions (pending stamp); late-committing events skipped by read view backfills (CommittedEventStore with LOCK TABLE events IN SHARE MODE, verified on PostgreSQL 18); an unbounded lock wait (lock_timeout plus retries); catch-up firing on ordinary lag (5 s stall, 10 s cooldown, 10 min deadline); plus smaller items (unknown privileges, invalid seqs, `require` in a goroutine, wrapped errors, `[]` not nil). **Deliberately deferred (tracked in Ambix):** stored-but-unpublished revocations (user decision: authz-service outbox); the readview library's commit-order gap for other services (upstream); removing the missing-header fallback and alerting on /authz 503s; moving Fetch after conn.Start in the 13 consumers (separate bump PRs).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01DVGsVQ8AMFR4NZoxyCoEqS
Reviewed-on: https://gitea.unbound.se/shiny/authz_client/pulls/333
## Summary
- `vladopajic/go-test-coverage@v2` (v2.18.5+, released 2026-04-26/27) restructured its composite action to pass inputs via env-var mapping. Gitea `act_runner` doesn't expand `${{ }}` expressions inside docker-action `env:` blocks reliably, so the literal string `${{ inputs.config }}` reached the binary and broke the 'Check coverage' step.
- Replace the action with a direct `go install` + binary invocation (matching the established Frostmoln pattern).
- Use `--github-action-output` to expose `total-coverage` as a step output, replacing the manual `go tool cover -func | grep | awk` calculations.
- Baseline artifact now stores the percentage directly instead of the full coverage profile.
- Bump `go` directive in `go.mod` from 1.22.12 → 1.26.2 (matching toolchain) — we are the sole consumers of this module.
## Test plan
- [x] `prek run --all-files` passes
- [ ] CI passes on this PR
- [ ] After merge, baseline artifact format propagates on next push to main
Reviewed-on: https://gitea.unbound.se/shiny/authz_client/pulls/303
## Summary
- Add `WithAPIKey(key string)` option to `PrivilegeHandler`
- When set, `Fetch()` sends `Authorization: Bearer <key>` header
- Backward compatible: no key = no header (existing behavior)
## Test plan
- [x] Unit test verifying Authorization header is sent
- [x] Unit test verifying no header without key
- [x] Existing tests still pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: https://gitea.unbound.se/shiny/authz_client/pulls/294
Add go-test-coverage for coverage threshold enforcement. Coverage data
is uploaded as artifacts on main branch and compared against baseline
in PRs using shell script that gracefully handles first run without
baseline. PR comments show coverage percentage.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove GitLab CI linter configuration and update module path from
`gitlab.com/unboundsoftware/shiny/authz_client` to
`git.unbound.se/shiny/authz_client` in all relevant files.
These changes reflect a migration to a new hosting service.
Adds multiple tests to verify the thread-safety of the
Fetch method and the handling of privileges in concurrent
operations. Tests include concurrent fetching, reading
privileges, and processing with multiple goroutines.
Ensures no errors occur during operations and verifies
privileges are set correctly.
Replace read lock with write lock in client.go to ensure thread
safety during the unmarshalling of JSON data. This prevents
concurrent read access and potential data races, improving
the integrity of the privileges data structure.
Introduce a default configuration file for git-cliff to manage
changelog generation. This file includes customizable templates
for the changelog header, body, and footer, and enables parsing
of commit messages according to the conventional commits
specification. The changes facilitate easier and standardized
documentation of project updates and changes.
Add support for the salary privilege in the privilege handler.
Implement associated logic to process and validate the
salary privilege in the test cases. Update the data
structures to include the new privilege and ensure
correct functionality in the privilege processing flow.
Add the 'Defaults.gitlab-ci.yml' pipeline template to the CI
configuration to standardize the build environment across
projects. This change ensures consistency and improves
maintainability in the CI process.
Adds a Setup method to PrivilegeHandler that configures AMQP consumers
for user and privilege events. This enables the handling of User.Added,
User.Removed, Privilege.Added, and Privilege.Removed events in a
streamlined manner, enhancing the event-driven capabilities of the
handler.
Updates the Go image in the CI configuration to use the
amd64 architecture version for better compatibility and
performance. This change ensures consistency across stages
that utilize Go tools, particularly for vulnerability testing.
Sets a timeout of 5 minutes for golangci-lint to improve
performance and prevent long-running linting processes.
This change ensures that the linting process completes within
a reasonable time frame, enhancing the development workflow.
Removes the deprecated pre-commit job from the CI configuration.
Adds a new template to streamline pre-commit tasks with Go. This
ensures a cleaner pipeline setup and improves maintainability.