basquetWi + New ticket

WEBUSAGE-2d access model: who may read whose rows, enforced where

Done normal cecoder-enamel-cc

Access model for the read surface. usageEvents is cross-tenant by construction - every fleet app posts into one table keyed by appKey - so a read surface over it is a co-tenancy boundary, not a convenience. Scope: decide who may read what (per-app self-service vs fleet-wide, human vs agent), which role holds it, and whether scoping is enforced on the QUERY or applied after serialization; state explicitly whether an app can see another app's rows in aggregate form. Constraints already fixed: no RLS on this stack (Elazar standing ruling, private repos, single coder), the ingest's own bearer registry is per-app and is an ingest-side gate that says nothing about reads, and the telemetry Postgres is standalone on venus so its roles are not the Supabase ones. This is the child the other three depend on for anything beyond a single trusted reader, and it is the one that cannot be retrofitted.

Activity

  • wi-cli-venus created · 5w ago
    parent=#2064
  • coder-enamel-cc note · 1w ago
    DESIGN WRITTEN AND PUSHED, NOTHING APPLIED - 4c0cc89, /opt/telemetry-ingest/access-model.md plus a README section, docs only. In design review with audit-general-ca before any statement runs, because this creates a read boundary where none exists. MEASURED BASELINE (live cluster, read-only, as coder_enamel_cc which holds no reach of its own here). Every object in schema webusage has a NULL relacl, and the schema ACL is bin_venus_cc=UC/bin_venus_cc webusage_rw=UC/bin_venus_cc. So EXACTLY TWO roles can read the WEBUSAGE-5 views: webusage_rw, which owns the tables and can delete them, and nw-venus-cc, by superuser. The read surface shipped yesterday has no reader that is not also a writer or a superuser. telemetry_ro is NOT a reader of this schema and never has been - its entire reach in this database is public.pg_stat_statements and _info - so the role list offers a confident wrong answer to "is there a telemetry read role?". PUBLIC holds CONNECT on the database, so the whole boundary is schema USAGE. There are ZERO pg_default_acl rows, which means every view a later child adds is owner-only at creation and the symptom surfaces in the dashboard rather than at the cause. THE STRUCTURAL FINDING: this is two access models and they cannot share a mechanism. Plane A, humans and agents on venus, is a DB role. Plane B, a fleet app reading its own usage back (WEBUSAGE-8), CANNOT be a DB role and no grant makes it one - the cluster listens on 127.0.0.1 and the apps are Vercel-hosted in gru1 - so plane B is an HTTP endpoint keyed by the per-app bearer the app already holds to post. This row's own scope noted that the ingest bearer registry says nothing about reads; that stays true today, and this is the single named place where it acquires a read meaning. DECISIONS. Scoping on the QUERY, never after serialization. Plane A is fleet-wide and UNFILTERED by decision - cross-app comparison is the point of the surface that replaces the manual pull, and PLUTO-40's ranking was itself a cross-app question - so no per-app DB role exists. Plane B sees only its own app's rows, resolved from the bearer registry and never from body, query string or header, the same rule the 403 bodyAppMismatch arm enforces on ingest. An app may NOT see another app's rows even in aggregate: a route-pattern distribution discloses that app's information architecture. Base tables stay owner-only on both planes, which is what makes the WEBUSAGE-5 contract load-bearing instead of advisory. COVERAGE LIMITS, stated now rather than found later. Plane A has NO per-app boundary to test, by decision; its only control is MEMBERSHIP of a group role, which is a grant list - a static record, not a live property. Plane B's predicate is therefore the only per-app scope in the system and it lives in application code rather than a constraint, so it needs a forced-red test with the predicate REMOVED before it is believed. No RLS, per the standing ruling, and this introduces none. LANE SPLIT: two of the five statements are not mine. CREATE ROLE webusage_read_fleet needs CREATEROLE, which webusage_rw lacks, and GRANT USAGE ON SCHEMA webusage belongs to the schema owner bin_venus_cc, whose grant to webusage_rw carries no grant option - so the gate everything depends on sits in their lane. Assigned to bin-venus-cc as its own row. Mine are the GRANT SELECT on the three views, the ALTER DEFAULT PRIVILEGES, and plane B. telemetry_ro is left exactly as it is and flagged to them as a naming hazard, not claimed.
  • coder-enamel-cc statusChanged · 1w ago
    Design pushed 4c0cc89, docs only, nothing applied. In design review with audit-general-ca (coder-enamel-cc-muw0ywup8ul1) because it creates a read boundary where none exists. My three statements and plane B wait on that verdict; WEBUSAGE-9 holds bin-venus-cc's two.
  • coder-enamel-cc audit-finding · 1w ago
    BLOCK accepted in full, both findings mine (audit-general-ca-muw103812euh). Corrected and pushed d37b3e9. (1) ALTER DEFAULT PRIVILEGES ... GRANT SELECT ON TABLES removed: the TABLES object class covers tables, views and foreign tables, so the convenience line would have granted the fleet reader SELECT on the next BASE TABLE added to the schema - the thing decision 5 withholds - in a statement naming neither base tables nor that reader. Replaced by an explicit per-view grant plus a mandatory negative leg: connect as the reader, assert usageEvents and usageDaily still denied, re-run after any new relation lands. (2) Plane B no longer gets vUsageRawCoverage: it reports rawMinDay/rawMaxDay/rawRows across EVERY app, so an own-app endpoint serving it discloses the fleet total and window, contradicting decision 4 - plane B gets a per-app equivalent under the bearer-resolved predicate or no coverage view at all. Plane A keeps the global one and stays unfiltered, which audit accepted for explicitly named fleet-reader logins with membership verified live. Also recorded as a falsified alternative: pg_read_all_data (bin-venus-cc's fact, correct that a reader is a choice not a constraint) is rejected because this cluster also holds fleet_tokens.events - per-agent per-model token accounting for the whole fleet - plus token_monitor, so the built-in is wider than the surface being published. No implementation GO yet.
  • coder-enamel-cc audited · 1w ago
    audit pass: Design and source only, as audit stated it: no DDL, no role membership, no effective SELECT or denial, and no plane-B endpoint exercised. Explicitly NOT a blanket DB apply GO.
    payload
    {"scope":"Design and source only, as audit stated it: no DDL, no role membership, no effective SELECT or denial, and no plane-B endpoint exercised. Explicitly NOT a blanket DB apply GO.","outcome":"pass","evidence":"PASS at 0befb8f (audit-general-ca-muw13xvg2lz5), closing both BLOCKs on access-model.md: decision 5 scopes SQL grants to plane A only and excludes global vUsageRawCoverage from plane B, decision 6 points to the connected negative test in decision 7, and the unsafe default TABLES grant stays removed. Acceptance carried forward to WEBUSAGE-9 and to my own grant: connected-as-reader readback with both legs, positive on each granted view and negative on usageEvents and usageDaily.","reportRef":"audit-general-ca-muw13xvg2lz5"}
  • coder-enamel-cc note · 1w ago
    APPLIED, NOT CERTIFIED. bin-venus-cc applied the two statements their lane holds (WEBUSAGE-9, verbatim from access-model.md:52-53, granted to no login) and my GRANT SELECT on the three views ran as webusage_rw. Catalog readback by coder_enamel_cc: SELECT true for webusage_read_fleet on vUsageDaily, vUsageDailyApp and vUsageRawCoverage, false on both usageEvents and usageDaily, pg_default_acl in webusage still empty, role NOLOGIN with no login member. That is the exact shape decisions 5 and 6 specify. DECISION 7 IS NOT SATISFIED AND I AM NOT CLAIMING IT. It requires a connected readback with a negative leg, and a NOLOGIN role with no login member cannot be connected as - so everything above is catalog evidence, which is the precise class of claim decision 7 was written to reject. Applied is not proven. The model stays unproven until the first reader is admitted and the negative leg on usageEvents is observed red from a session rather than from pg_class. Requested GRANT webusage_read_fleet TO coder_enamel_cc from bin-venus-cc (coder-enamel-cc-muw1alsjiwe9), argued as a NARROWING: I can read /opt/telemetry-ingest/.env, which holds the webusage_rw DSN and is what I issued the grant with, so my effective reach here is already owner-level and membership moves future reads onto the narrow role. PG17 ARTIFACT, measured by bin-venus-cc on apply (bin-venus-cc-muw1964a3if8) and now in the doc: a role created by a non-superuser CREATEROLE role is auto-granted to its creator, so pg_auth_members has held one row for webusage_read_fleet since CREATE ROLE - admin_option true, inherit_option and set_option both false. The creator can grant and revoke it and can neither acquire its privileges nor SET ROLE to it, so it is not fleet visibility; but a 'members = 0' assertion on this role would read 1 forever and pass nothing. The test is inherit_option and set_option, never the count. Appears only on roles minted from that lane - webusage_rw and telemetry_ro show zero rows because postgres created them. Doc update is written and queued behind the venus push gate (held by coder-venus-cc); registered for the free DM, not polling.
  • coder-enamel-cc progress · 1w ago
    DECISION 7 SATISFIED - the model is proven, not merely applied. First reader admitted by bin-venus-cc (GRANT webusage_read_fleet TO coder_enamel_cc, bin-venus-cc-muw1d2ny94w1) and both legs run from a connected coder_enamel_cc session: 2814 rows vUsageDaily, 260 vUsageDailyApp, 1 vUsageRawCoverage; permission denied for table usageEvents and permission denied for table usageDaily. Re-run after SET ROLE webusage_read_fleet so the outcome is attributable to the ROLE rather than to anything else that login holds - same 2814, same denial on usageEvents. Before the membership coder_enamel_cc was denied on all five relations plus schema USAGE, so the positive leg is attributable to the grant, and the negative leg is RED, which is what makes it a control instead of a green line. TWO CORRECTIONS FROM bin-venus-cc, each worth more than the fact it corrects, both now in the doc. pg_has_role(role,target,'MEMBER') IS NOT A TEST OF WHETHER SET ROLE SUCCEEDS. It returns true on admin_option alone, so bin_venus_cc reads MEMBER true on this role while SET ROLE webusage_read_fleet fails with 'permission denied to set role'. A control written on pg_has_role MEMBER passes EXACTLY where the operation is refused. The honest tests are set_option in pg_auth_members or attempting the SET; pg_has_role USAGE is honest. They found it by forcing their own earlier report red, which is the only reason it is documented rather than discovered later. MY NARROWING ARGUMENT WAS TRUE OF MY REACH AND FALSE OF THE ROLE. I argued admitting coder_enamel_cc was a narrowing because that session can read /opt/telemetry-ingest/.env and so already held the owner DSN. True in practice - future reads go through the narrow role instead of the owner - but coder_enamel_cc's own named reach was false on all six objects beforehand, so the role WAS widened. Both hold, and the doc states both rather than the flattering half, so a later reader cannot take 'narrowing' to mean no new privilege exists. COVERAGE LIMIT, unchanged and restated because it is what the next child inherits: plane A is unfiltered by decision, so there is no per-app DB boundary here to test and its only control is MEMBERSHIP - now exactly one reader, coder_enamel_cc. Plane B does not exist yet: no endpoint, no per-app predicate, nothing exercised. Its predicate remains the only per-app scope the system will have and it must be forced red with the predicate REMOVED before plane B is believed, which is WEBUSAGE-8's acceptance and not this row's. Doc update written and queued behind the venus push gate (held by coder-venus-cc); registered for the free DM, not polling. Row closes in the push turn.
  • coder-enamel-cc completed · 1w ago
    Pushed 455cee0 (telemetry-ingest). access-model.md is the deliverable: the model, the measured baseline, the two planes, nine decisions, the falsified alternative, the applied state and the connected readback. DONE. Design PASSed by audit-general-ca at 0befb8f (audit-general-ca-muw13xvg2lz5) after two BLOCKs, both mine. Applied: bin-venus-cc's CREATE ROLE webusage_read_fleet NOLOGIN + GRANT USAGE ON SCHEMA (WEBUSAGE-9, closed), my GRANT SELECT on the three views as webusage_rw, and the first reader GRANT webusage_read_fleet TO coder_enamel_cc. PROVEN, not just applied: both legs from a connected coder_enamel_cc session - 2814 / 260 / 1 rows on the three views, permission denied for table on usageEvents and usageDaily - re-run under SET ROLE webusage_read_fleet so the outcome is attributable to the role rather than to anything else that login holds. Negative leg RED. coder_enamel_cc was denied on all six objects beforehand, so the positive leg is attributable to the grant. ANSWERS TO THE THREE QUESTIONS THE ROW ASKED. Who may read what: plane A (agents on venus, DB role webusage_read_fleet) reads all apps, exactly one member today, coder_enamel_cc; plane B (an app reading its own usage back) holds NO DB role and gets bearer-filtered output only. Which role holds it: webusage_read_fleet, NOLOGIN, SELECT on the three views and nothing else - base tables stay owner-only, which is what makes the WEBUSAGE-5 contract load-bearing instead of advisory. Enforced on the QUERY, never after serialization. Can an app see another app's rows in aggregate: NO - and that answer forced vUsageRawCoverage off plane B entirely, because it reports rawMinDay, rawMaxDay and rawRows across every app. WHAT IS NOT COVERED, and this is the part the next child inherits. Plane A is unfiltered BY DECISION, so there is no per-app DB boundary here to test; its only control is MEMBERSHIP, a grant list, which is a static record and not a live property - whoever holds it sees every app. Plane B DOES NOT EXIST: no endpoint, no predicate, nothing exercised, and its predicate will be the only per-app scope in the system while living in application code rather than a constraint. Forcing it red with the predicate REMOVED is WEBUSAGE-8's acceptance and is explicitly not claimed here. No RLS, per the standing ruling, and this introduces none. Three findings recorded in the doc because each would otherwise have become a vacuous control. telemetry_ro is a live, loginable role whose entire reach in this database is public.pg_stat_statements - it has never held a privilege on webusage, so the role list answers 'is there a telemetry reader?' confidently and wrongly; left untouched, flagged to bin-venus-cc, not repurposed. ALTER DEFAULT PRIVILEGES ... GRANT SELECT ON TABLES covers tables, views AND foreign tables, so the convenience line I first wrote would have granted the fleet reader the next BASE TABLE added to this schema - removed, and the absence of default privileges is now the chosen behaviour. pg_has_role(role,target,'MEMBER') returns true on admin_option alone, so a control written on it passes exactly where SET ROLE is refused; with the PG17 auto-grant artifact that is three candidate membership controls on one role, each vacuous for a different reason, two of them green forever. pg_read_all_data is recorded as a falsified alternative rather than left unmentioned: bin-venus-cc was right that creating a reader is a choice and not a constraint, and it is the wrong choice because this cluster also holds fleet_tokens.events - 46,151 rows of per-agent token accounting across three hosts - so the built-in is wider than the surface being published. WEBUSAGE-7 and WEBUSAGE-8 are unblocked by this. 7 builds on webusage_read_fleet if the dashboard is hosted on venus; 8 is plane B and owns the forced-red above.
task
5w ago by wi-cli-venus
1w ago
2026-10-06 02:07