Strict read profile
PRIVACY_READ_PROFILE selects the read privacy policy for transactions, receipts and events. It applies deployment-wide, to JSON-RPC and to the Explorer Data API (proxy mode) alike, and is fixed at startup.
| Value | Policy |
|---|---|
standard (default) | The policy described in Response Filtering and Privacy Requirements. Contract and org admins can read transactions sent to their contracts and all events of their contracts, visibleTo shares a transaction with its listed recipients, and the per-contract visibleTo unlock shares full event payloads. |
strict | A transaction or receipt reaches only a transaction participant. An event reaches only a party that one of its indexed address parameters names. Administrative authority, visibleTo and participation grant no other read access. |
Any other value, including a value of only spaces, refuses to start. When the environment variable is empty, the CONFIG_FILE value applies, and without one the profile is standard.
What strict enforces
A participant is a viewer whose linked address is the transaction's from or to. Nothing else counts as participation: an address in the calldata, in an event, or in a token transfer does not.
| Data | Who receives it under strict | Everyone else |
|---|---|---|
Transaction object: eth_getTransactionByHash, eth_getTransactionByBlockHashAndIndex, eth_getTransactionByBlockNumberAndIndex, full-object eth_getBlockByHash / eth_getBlockByNumber | Participants | null, or omitted from the block |
Receipt: eth_getTransactionReceipt, eth_getBlockReceipts | Participants | null, or omitted from the list |
| Explorer transaction pages, transaction lists, block and address feeds, counts and statistics | Participants | Not found (404) / not listed / not counted |
| Internal calls: explorer internal transactions | Participants of the parent transaction | Not listed |
debug_traceTransaction | Participants, as the top-level call frame only: the call tracer without internal frames or logs. Any other tracer or tracer setting is refused (HTTP 400) | Not found (HTTP 404), the same answer as for an unknown transaction hash |
Events: eth_getLogs, the logs in a receipt, explorer log endpoints | A viewer named by an indexed address parameter of the event (see below) | Removed |
| Token transfers (explorer) | A viewer the underlying Transfer event names, under the same rule as events. The viewer's own side is shown. The other side is shown only when it is visible to the viewer by itself, such as a contract or the zero address; another user's address shows as [PRIVATE]. The amount is shown | Not listed |
| Token-transfer count and category on an explorer transaction row | Not shown to anyone. The count would reveal how many transfers the event rule hides; the transfers view lists the ones the viewer may see | Not shown |
| Search suggestions for a transaction hash | Viewers who may open that transaction | Not suggested |
The event rule
Under strict an event is returned only when all of these hold:
- The emitting contract has a registered ABI (a custom upload, or a
metadata.token_typeof the built-in registry), and it declares the event's signature (topic0) as a standard, non-anonymous event. - One of the event's indexed
addressparameters equals one of the viewer's linked addresses. - The viewer holds a contract grant on the emitting contract whose event rules allow the event. For an org admin or a contract admin, admin authority stands in for the event rules. It never stands in for condition 2.
- The dynamic-payload gate passes. See
events_allow_dynamic_payloadin Response Filtering.
The event is hidden when any of the following holds:
- the ABI is unknown or does not declare the event;
- the event is anonymous;
- the topics do not match the declared indexed parameters;
- an address topic is not canonically padded;
- the data has undeclared bytes or does not decode exactly against the ABI;
- multiple event layouts in the ABI fit the same log, leaving its fields ambiguous.
Addresses of other users embedded in an admitted event stay masked, as under standard.
Events without an indexed viewer address are hidden from everyone
Under strict, an event that does not carry the reader's address as an indexed address parameter is hidden from everyone, including the contract's admins and the transaction's own sender. This covers:
- business events keyed by an identifier, for example
PaymentCompleted(bytes32 indexed paymentId); - lifecycle events such as
Initialized(uint8)orUpgraded(address indexed implementation), where the implementation is a contract and not a user; - events whose address parameter is not indexed, for example
Paused(address account).
Applications that rely on such events do not see them under strict. Emit each party that must see the event as an indexed address parameter. Disclosure grants do not change ordinary event admission.
What strict switches off
| Mechanism | standard | strict |
|---|---|---|
| Contract / org admin reading other users' transactions and receipts | Allowed | Off |
| Admin reading all events of the admin's contracts | Allowed | Off. Events that index the admin's own address remain visible |
ORG_ADMIN_VIEW_USER_TXS audit view | As configured | No effect. A startup warning is logged when it is set |
visibleTo sharing of a transaction and its receipt | On | Off. A JSON-RPC send that carries a visibleTo (or privateFor) field is rejected with HTTP 400, whatever the field lists, before it is traced or forwarded, and nothing is stored |
visibleTo event fallback and the per-contract visibleTo unlock | On | Off |
| A participant's own address-less events | Visible | Hidden (see the warning above) |
| Receipt for a viewer entitled to one of its events | Returned | null |
| Explorer participation via calldata or event topics | On | Off |
| View as user | Available | Refused (HTTP 403), audited |
| Admin dry-run of read methods | Available | Refused (HTTP 403), audited. A dry-run of a send returns only the allow/deny verdict, without the trace or its logs |
| Admin test-request of a transaction, receipt, log or block read | Returns the node's answer | Refused (HTTP 403) before the node is called, and recorded in the access log. Address-state reads are still served |
debug_traceTransaction | The call tree (callTracer) for a participant, an admin of the contract the transaction called, or a visibleTo recipient, and only when the caller has access to every contract in the returned call tree. Any other caller with trace access is refused (HTTP 403) | The top-level call frame only (see above), for a participant. Anyone else gets not found |
Approved selective disclosure grants apply only through dedicated grant endpoints. They add no access to regular JSON-RPC or Explorer reads. The Explorer UI combines regular transactions with scoped grant transactions.
Residual surfaces the profile does not govern
The read profile covers transactions, receipts, events and traces. It does not change:
- Address state.
eth_getBalance,eth_getTransactionCount(an account's nonce),eth_getStorageAt,eth_getProofandeth_callare governed by the method allowlist and the per-address checks. Org admins also keep access to historical-state queries. debug_traceCall. This simulates a call the caller composes and is governed by the method allowlist and trace validation.- Operator-added RPC namespaces. Methods enabled through
EXTRA_RPC_NAMESPACES_FILEare not covered by the profile's response filters. Do not enable methods that return transactions, receipts, logs or traces (for exampletrace_*orots_*) on a deployment that must runstrict. - Block position of a receipt. A participant's receipt carries
transactionIndexandcumulativeGasUsed, which reflect how many transactions, and how much gas, precede it in the block. - Compliance records. Compliance decision logs and travel-rule records in the admin API remain readable by org admins.
- Access-log parameters. With
AUDIT_LOG_PARAMS=true, the access log records redacted request parameters, which can include transaction hashes and addresses.
Assess these separately when a deployment must meet the strict requirement end to end.
Enabling and proving the profile
-
Set
PRIVACY_READ_PROFILE=strictin the proxy environment and redeploy. IfORG_ADMIN_VIEW_USER_TXS=trueis also set, it has no effect. -
Check the startup log for
read privacy profile profile=strict. -
Query the effective profile. The endpoint accepts any admin credential:
curl -s -H "X-Admin-Token: $ADMIN_API_TOKEN" http://<proxy>/api/v1/admin/system/read-profile # {"profile":"strict","org_admin_view_user_txs":false,"org_admin_view_user_txs_configured":false} -
Run the smoke probe against the running deployment. Use the token of a viewer who is not a participant of a known transaction, such as an admin of the contract it called:
PROXY_URL=http://<proxy> ADMIN_AUTH="X-Admin-Token: $ADMIN_API_TOKEN" \ VIEWER_JWT=<non-participant token> TX_HASH=<transaction hash> \ EXPLORER_URL=http://<proxy> scripts/check-strict-read-profile.shThe probe checks four things:
- the reported profile;
- that
eth_getTransactionByHashandeth_getTransactionReceiptreturnnullto that viewer; - that
debug_traceTransactionreturns no trace; - optionally, that the Explorer Data API answers 404.
It exits non-zero on any failure.
-
Run the block explorer in proxy mode (
PRIVACY_PROXY_URL), and make sure clients cannot reach the node or the chain indexer directly.
Changing the profile back
The profile is applied when data is read, not when it is written. A switch from strict to standard immediately applies the standard policy to all existing data again: admin reads, the per-contract unlock, and so on. Treat a downgrade as a disclosure event. It needs change-management approval and an audit record, and the startup log records the profile each deployment runs with.