Granted read scope
BurnLedger has always checked that a customer-supplied credential cannot write. It never reported what that credential may read.
The two are not the same question, and only one of them was being asked. A
customer who hands over a warehouse-wide GRANT SELECT ON *.* passes every
admission gate: the credential is genuinely read-only, the transport is
genuinely verified, and the registered query genuinely touches one table. Nobody
— not the operator running the registration, not the customer who issued the
credential — was shown that the grant reaches every schema on the server.
Read scope is that disclosure.
What it is not
It is not enforcement. No connection is refused for being broadly scoped, and none ever will be by this mechanism. BurnLedger cannot know what else a credential is legitimately used for, and a product that started rejecting working credentials on a guess would be worse than one that says nothing.
It is not attested. Like transport_security before it, the read scope is
operator-facing diagnostics. It is not bound into the signed attestation
payload, so a relying party holding a certificate cannot check it. It describes
the grants at the moment of the check and says nothing about the grants an hour
later.
An empty scope does not mean "reads nothing." It means the scope was not
established — usually because the engine will not answer the question. Read it
beside read_only_enforcement, which says whether anything was introspected at
all.
Where it surfaces
| Surface | Field |
|---|---|
POST /v1/systems/test-connection |
read_scope (array of strings), and appended to the read_only step's detail |
| Enclave health check | HealthCheckResponse.read_scope |
Audit log, system.registered |
read_scope in the entry's metadata |
It is deliberately not a column on the systems table. A column would claim
to describe the credential as it is now, and would be wrong the moment a grant
was widened without telling BurnLedger. An audit entry claims only what was true
when the system was admitted, which is what was actually measured — and it is
what an auditor asking "what could this credential see when it was registered"
needs.
Coverage
Reporting the scope requires the engine to answer a question about its own grants. Some will, some will not, and some can be asked but have not been wired up yet. The table states which is which, because a blank field whose reason is unstated is not a disclosure.
Reports granted read scope
| Connector | Source | Shape |
|---|---|---|
| PostgreSQL | information_schema.role_table_grants |
SELECT on 1284 tables in schema public — one line per schema, so a warehouse-wide grant stays readable |
| MySQL | SHOW GRANTS FOR CURRENT_USER() |
SELECT ON *.* — privileges and object, grantee dropped |
| SQL Server | HAS_PERMS_BY_NAME over sys.objects |
SELECT on 412 tables in schema dbo — one line per schema, views on their own line (SELECT on 3 views in schema dbo) because a view can read tables the login holds no direct grant on |
| Redis | ACL GETUSER |
ACL rules: -@all +@read ~user:* — verbatim, because the rules are the scope in the operator's own vocabulary |
| Elasticsearch | _security/user/_privileges |
read on logs-* — the index pattern is the whole disclosure |
| Oracle | USER_TAB_PRIVS ∪ ROLE_TAB_PRIVS, plus SESSION_PRIVS |
SELECT on 3 tables in schema APPOWNER — one line per owning schema; a system privilege that reads everything is its own line, SELECT ANY TABLE (system privilege). Verified against the shared Oracle Free container under the guide's grant. Roles not enabled by default are counted if ROLE_TAB_PRIVS shows them |
| Redshift | pg_class through has_table_privilege |
SELECT on 12 tables in schema analytics — the PostgreSQL shape, but not the PostgreSQL query: Redshift has no role_table_grants or pg_has_role. Verified against the PostgreSQL container the Redshift suite runs on, not against a live Redshift cluster |
| Teradata | DBC.AllRightsV ∪ DBC.AllRoleRightsV (roles walked recursively, as the write check does), RETRIEVE right only |
SELECT on 2 tables in database appdb, or SELECT on all tables in database appdb for a database-level grant (TableName = 'All'). Verified against a recorded Query Service response, not against a live Teradata system |
| Snowflake | SHOW GRANTS TO ROLE <current role> — the statement the write check already runs |
SELECT on 4000 tables in ANALYTICS.PUBLIC, SELECT on 1 view in ANALYTICS.PUBLIC — one line per schema and object kind. Does not expand the role hierarchy or SHOW FUTURE GRANTS. Verified against recorded SHOW GRANTS output, not against a live Snowflake account |
| Databricks | INFORMATION_SCHEMA.TABLE_PRIVILEGES in the configured catalog |
SELECT on 4000 tables in main.sales — a catalog- or schema-level SELECT is inherited onto every table there, so this is the effective breadth in tables. The configured catalog only; group-carried grants are not expanded; hive_metastore has no information schema and reports nothing. Verified against a recorded aggregate, not against a live workspace |
| Cassandra | LIST ALL PERMISSIONS OF <role> — the statement the write check already runs |
SELECT on <table myapp.user_events>, SELECT on <keyspace myapp> — the resource verbatim, as cqlsh shows it, because a keyspace grant does not enumerate its tables. Empty under AllowAllAuthorizer, where the statement is refused and read-only rests on the operator's assertion; that pairing is what the container test pins |
| DynamoDB | ListTables + iam:SimulatePrincipalPolicy for dynamodb:Query on each table ARN |
Query on table UserProfiles — one line per table; when the credential cannot ListTables (the documented least-privilege policy does not grant it) the list carries ListTables not granted; only the registered table could be checked, so one table is never mistaken for the measured breadth. Nothing against a DynamoDB-compatible endpoint, which has no IAM to ask |
| MongoDB | connectionStatus with showPrivileges |
find on all collections in app — one line per resource the credential may find on, in MongoDB's own resource vocabulary: app.users, all collections in app, all databases, any resource. find is the action that returns documents; the write check has already refused everything outside the read role's set |
| Neo4j | SHOW USER PRIVILEGES AS COMMANDS |
GRANT MATCH {*} ON GRAPH * NODE * — the ACCESS, MATCH, READ and TRAVERSE commands verbatim, DENY rows included because Neo4j applies them over any grant, role name dropped. Enterprise only: Community Edition has no privileges to show |
| S3 | ListBuckets, and one page of ListObjectsV2 from the bucket root |
s3:ListAllMyBuckets: 47 buckets visible and s3:ListBucket on uploads: 1000+ top-level prefixes — the two things a credential can hold beyond the one prefix a registered query touches. A refused call is the absence of its line. An S3-compatible store may answer ListBuckets with the reachable subset instead of refusing (MinIO does), which is still the credential's reach |
| GCS | one page of objects.list from the bucket root |
storage.objects.list on bucket uploads: 12 top-level prefixes — whether a prefix-conditioned IAM binding confines the credential, or it lists the whole bucket. Other buckets in the project are not measured: the connector holds no project id to list them under |
| BigQuery | datasets.list, and tables.list on the configured dataset |
3 datasets visible in project p and 12 tables in dataset ds. The service filters datasets.list to what the caller may access, which is what puts a project-level roles/bigquery.dataViewer beside a dataset-scoped role — the dataset ACL cannot, because project-level grants never appear in it. tables.list is not in the guide's custom role and its line is absent under it. No emulator exists: the probe is tested against a recorded REST fixture, not the service |
Does not report it yet, but could
Nothing, as of this revision: every connector that introspects privileges for the write check now reports the read side with the same query or one more.
Cannot report it
These have no privilege introspection reachable by the query credential, which
is the same reason their read-only status rests on the operator's read_only
assertion rather than on anything BurnLedger verified. For these four, a blank
read scope is the honest answer and not a gap to close.
| Connector | Why |
|---|---|
| Azure Blob | Azure exposes no data-plane permission simulation |
| MarkLogic | Privilege introspection needs the Management REST API, which the query credential cannot reach |
| HBase | No permission introspection over its wire API |
| Redis (when ACL is unreadable) | Redis 5 and earlier have no ACL system; the scope is reported only when ACL GETUSER answers |
Bounds
A single check reports at most 32 entries, each at most 200 characters. A truncated list ends with a marker naming how many were hidden — showing 32 of 1,284 without saying so would understate exactly the breadth this field exists to show. Entries are deduplicated and sorted, so two checks against an unchanged credential produce an identical value and an auditor diffing two registrations sees a difference only when the grant actually changed.