@@ -192,31 +192,38 @@ add CODEOWNERS-based merge gates until multiple independent maintainers exist.
192192 administrative, replication, maintenance, grant-capable, membership-delegating,
193193 executable-privileged, view-mediated-RLS-bypass, materialized-copy, foreign-data,
194194 and owner-capable login sessions are outside the application isolation guarantee.
195- - The write path must close the admission-to-write DDL race rather than treating live
196- catalog admission as transferable to a later statement. ` enqueue_in_transaction() `
197- must acquire `LOCK TABLE ONLY public.llm_context_lifecycle_outbox IN ROW EXCLUSIVE
198- MODE` before live authority admission and retain it through the caller transaction,
199- including the durable ` INSERT ` . This deliberately pulls forward the table lock that
200- PostgreSQL ordinary DML already uses: concurrent ` CREATE TRIGGER ` and other
201- conflicting schema DDL must wait instead of changing executable row authority after
202- admission. Do not substitute a package advisory lock, a second post-hoc catalog
203- check, or ` ACCESS EXCLUSIVE ` ; the former does not participate in PostgreSQL table-DDL
204- locking and the latter needlessly serializes compatible application DML. The normal
205- application role still needs only the existing non-grantable ` SELECT ` and ` INSERT `
206- privileges, which are sufficient for ` ROW EXCLUSIVE ` . Lock acquisition and
207- contention are part of the complete buyer-path latency measurement, not removable
208- security overhead.
209- - The read path must close the admission-to-read relation-identity race rather than
210- relying on a later ` SELECT ` to acquire relation authority after admission.
211- ` load_in_transaction() ` must acquire `LOCK TABLE ONLY
195+ - The write path must close both the admission-to-write DDL race and qualified-name
196+ rebinding. ` enqueue_in_transaction() ` must acquire `LOCK TABLE ONLY
197+ public.llm_context_lifecycle_outbox IN ROW EXCLUSIVE MODE` before live authority
198+ admission and retain it through the caller transaction, including the durable
199+ ` INSERT ` . The lock protects the admitted relation object from conflicting table-
200+ program/relation DDL, but it does not by itself authenticate an independently
201+ mutable schema/name binding. ` _require_rls_application_role() ` must return the exact
202+ validated lifecycle-outbox ` pg_class.oid ` ; the write must carry that admitted OID
203+ into the same data-modifying CTE that can perform the ` INSERT ` , resolve the live
204+ qualified name with ` pg_catalog.to_regclass(...) ` , and execute the write only when
205+ the live OID equals the admitted OID. A standalone identity recheck followed by a
206+ separate ` INSERT ` is another TOCTOU interval. Do not substitute a package advisory
207+ lock, caller ` search_path ` , schema qualification alone, or ` ACCESS EXCLUSIVE ` .
208+ The normal application role still needs only the existing non-grantable ` SELECT ` and
209+ ` INSERT ` privileges. Lock acquisition/wait and the live OID proof are part of the
210+ complete buyer-path latency measurement, not removable security overhead.
211+ - The read path must close both admission-to-read relation-object and namespace-name
212+ races. ` load_in_transaction() ` must acquire `LOCK TABLE ONLY
212213 public.llm_context_lifecycle_outbox IN ACCESS SHARE MODE` before live authority
213214 admission and retain it through tenant binding, optional tenant/event advisory
214- serialization, and the consuming ` SELECT ` . Concurrent ` ACCESS EXCLUSIVE ` DDL must
215- wait rather than rename, replace, or drop the admitted relation between the catalog
216- proof and data access. Do not widen ordinary reads to ` ROW EXCLUSIVE ` or
217- ` ACCESS EXCLUSIVE ` ; ` ACCESS SHARE ` is the minimal fence and remains compatible with
218- ordinary reads and writes. Treat its acquisition and wait as part of complete
219- buyer-path latency evidence, not removable security overhead.
215+ serialization, and the consuming ` SELECT ` . ` ACCESS SHARE ` protects the admitted
216+ relation object against conflicting relation DDL, but it does not authenticate the
217+ independently mutable ` public ` schema/name binding. ` _require_rls_application_role() `
218+ must return the exact validated lifecycle-outbox ` pg_class.oid ` , and the consuming
219+ read itself must resolve the live qualified name with ` pg_catalog.to_regclass(...) ` ,
220+ require that live OID to equal the admitted OID, and accept a durable row only when
221+ its ` tableoid ` is the admitted OID. Keep the identity comparison in the statement
222+ that can return evidence; a standalone pre-read recheck only creates another TOCTOU
223+ interval. Do not widen ordinary reads to ` ROW EXCLUSIVE ` or ` ACCESS EXCLUSIVE ` , and
224+ do not treat schema qualification, caller ` search_path ` , or the package advisory
225+ lock as object identity. Treat lock acquisition/wait and the live OID proof as part
226+ of complete buyer-path latency evidence.
220227- Migrations must restore forced RLS within the same atomic SQL statement that
221228 relaxes owner enforcement, preserve legacy rows under ` standalone ` , remain
222229 idempotent, and keep the packaged and Docker initialization schemas
0 commit comments