Skip to content

feat: add subject access logs and appointment references - #1766

Merged
jeremi merged 9 commits into
mainfrom
feat/subject-access-and-appointment-links
Sep 30, 2026
Merged

jeremi merged 9 commits into
mainfrom
feat/subject-access-and-appointment-links

Conversation

@jeremi

@jeremi jeremi commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Summary

Closes #1763.
Closes #1764.

BReg can now keep a subject-facing access log for an opted-in entity. Each materialized record read records the requester, time, and purpose under that registry's record identifier. The subject reads it with their own token and current record authority. Retention defaults to 90 days; governed exemptions delay visibility and record their use in the operational audit. Evidence can forward the original verified requester and authorized purpose to explicitly configured sources.

Scheduling's v1alpha2 HTTP contract adds typed, opaque externalReferences to holds, direct bookings, and appointment responses. References survive confirmation, rescheduling, cancellation, and retries. An authenticated caller can list their appointments by an exact product, record type, and identifier tuple.

Evidence

  • BReg PostgreSQL: postgres_access_log (5), postgres_migration (52), postgres_compiled_schema (6), postgres_read (3), and postgres_startup (15) passed with --locked --features postgres-test,tooling,schema. Coverage includes owner concealment, trusted attribution, per-hit logging, failure before response release, delayed exemptions, relationship profile collisions, expiry privileges, and upgrading an older catalog before later disabling collection. Three opt-in performance benchmarks were not run.
  • BReg compiler access-log tests (9), runtime library tests (501), product contracts, generated artifacts, and client contract gate passed. cargo clippy --locked --all-targets passed for BReg and bregctl with runtime/PostgreSQL features.
  • Evidence: all 39 source_contracts tests, authenticated requester attribution, batch paths, reserved-header refusals, clippy, configuration-key checks, source neutrality, and contract reproduction passed. The maintained BReg-to-Evidence composition proof passed its 22 fixture cases and native source update.
  • Scheduling: focused PostgreSQL lifecycle and populated-v9 migration tests passed, as did legacy retry/receipt compatibility, owner/filter-bound paging, admission validation, client HTTP tests, check/clippy, and generated OpenAPI/contract checks.
  • Disposable native journeys passed: BReg create/list/get and subject-log retrieval with a wrong-subject 404; Evidence HTTP 200 with offline VERIFIED, plus a tampered-purpose 403. Formatting and diff checks passed.

The full cross-product PostgreSQL matrices and TLS/S3 variants were not repeated locally. Focused tests cover the changed boundaries; protected CI supplies the broader matrix.

Notes

  • Subject logs contain requester and purpose values and have separate storage, reader rules, and retention from the pseudonymized operational audit. HTTP access requires current GET authority and stored subject ownership in one database snapshot. Forwarded headers convey attribution only and require a configured, verified intermediary client.
  • BReg package activation installs the managed log table and bounded expiry function. Policy changes are classified as access/disclosure changes. Disabling collection preserves existing rows until expiry. A log entry records a materialized read, even if a later audit or network failure prevents response delivery.
  • Scheduling migration 10 gives existing claims an empty reference set. Existing empty-reference idempotency hashes and stored receipts remain readable. References are identifiers only, confer no authority, and never trigger calls to another product. Upgrade the runtime before sending the new admission field; rescheduling retains the original references.

DCO

  • Every commit includes a Signed-off-by trailer.
  • I reviewed the submitted changes and am responsible for the contribution.

Review follow-up

  • Retention backlog: one expiry tick now erases expired access-log rows in committed batches of 1000 until a batch comes back short, bounded at 100 batches (100,000 rows) per tick. When the bound is hit, the tick logs a warning with the remaining expired backlog, and the next tick continues. A PostgreSQL test seeds 2,500 expired rows and proves one bounded tick reports the backlog and the next unbounded tick drains it without touching a live row.
  • Read coverage: PostgreSQL tests now prove that revision, snapshot, attachment download, and GIS collection item reads each write access-log rows, and that the create path writes none. products/breg/ACCESS-LOG.md now names GIS collection items under list reads; no existing claim in it was false.
  • Docs: the retention and persistent-state page describes the access-log table and its expiry job.
  • The identifier catalog is regenerated for the changed BReg authoring schemas.
  • Gates on the pushed head: cargo fmt --check; cargo clippy --locked -p registry-breg --all-targets --features postgres-test,tooling,schema -- -D warnings; registry-breg library (173) and access_log (9) tests without a database; postgres_access_log (7) and postgres_migration (52) on PostGIS, plus the new attachment and GIS tests; products/identifiers/scripts/check.sh; generate.py --check-references; docs npm test, npm run check, and check:evidence-anchors.

Security review notes

  • Subject ownership and row visibility are checked in one database snapshot, so the subject's access-log route cannot combine an ownership answer and a visibility answer from different moments.
  • Logging fails closed: a failed access-log insert fails the read, and no response is released for a read that was not logged.
  • The subject's access-log route refuses requests that carry the requester or purpose forwarding headers.
  • Evidence forwards requester attribution only to sources that opt in per source, and the attribution headers are reserved to the runtime. BReg accepts forwarded attribution only from a verified client the entity names as a trusted intermediary.
  • On direct reads without forwarded attribution, the logged requester is the authenticated client, falling back to the caller's principal. A governed exemption's exemptionReason becomes visible to the subject once the exemption's delay ends. Both are deliberate, documented product choices.
  • The Evidence export protocol moves to breg-evidence-lookup-v2, which changes the behaviorRevision of exported sources.
  • The Scheduling listing by external reference returns only the caller's own bookings, and its cursor is bound to the same caller and exact reference, so a cursor cannot be moved across either boundary.
  • The retention fix bounds the work of one expiry tick and makes an unfinished backlog visible in the logs, instead of leaving expired purpose values in the live database longer than the documented expiry.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…ring schemas

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
One expiry call erases at most 1000 rows, and the worker ran it once a
minute, so a registry logging more than 1000 reads a minute accumulated
expired rows past their retention promise. Each tick now keeps erasing
committed batches until one comes back short, stops after 100 batches, and
logs the remaining backlog when it reaches that bound.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
… access-log entries

ACCESS-LOG.md lists these read paths as logged, but the PostgreSQL suites
only proved get, list, lookup, and relationship reads. Each path now has a
test that reads through the real route and finds its access-log row.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
…ks read its result

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi

jeremi commented Sep 29, 2026

Copy link
Copy Markdown
Member Author

Overnight review: the blockers and the retention backlog are fixed in commits 89f62b1 through 8ae9467, and the details are in the updated PR body. The remaining non-blocking findings are filed as #1768 (BReg access log) and #1769 (Scheduling references).

@jeremi
jeremi enabled auto-merge September 29, 2026 23:48
@jeremi
jeremi added this pull request to the merge queue Sep 30, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 30, 2026
The closed catalog now always expected the subject access-log table and
expiry function, but only an apply creates them. A database v0.37.0
activated therefore failed `bregctl instance-claim status`, `bregctl
verify`, and `breg` startup before any successor could be applied, and
reapplying the active package failed because installing the storage
changes the fingerprint that package records.

A compiled catalog whose registry declares no `accessLog` is now checked
without that storage while its table is absent. The next successor apply
installs it. The retention worker starts only where the storage exists.

Security review note: this relaxes the closed-catalog check only for the
two access-log objects, only for a registry that collects no access log,
and only while the table is absent. Ownership, ACL, row security, policy,
and the recorded fingerprint checks stay exact, so a partial install or a
collecting registry without storage is still refused.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi
jeremi enabled auto-merge September 30, 2026 00:54
@jeremi
jeremi added this pull request to the merge queue Sep 30, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 30, 2026
@jeremi
jeremi added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 4007a2f Sep 30, 2026
59 checks passed
@jeremi
jeremi deleted the feat/subject-access-and-appointment-links branch September 30, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant