Task: Gate and seed the marketdata permissions
Table of Contents
This page documents a task in the Rewrite the oresmd grammar as a hand-written codec checked against ORE story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
Every permission a marketdata handler checks is seeded, granted to the roles that need it, and checked by the gate.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Rewrite the oresmd grammar as a hand-written codec checked against ORE |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-02 |
3. Acceptance
marketdatais inREGISTRYincheck_handler_permissions.py, and the gate passes.- All 17 codes the handlers check are seeded. Codes no handler checks are removed or given a handler.
- Each role that calls a marketdata handler holds the code it needs, without the
marketdata::*wildcard.SyntheticServiceis checked against what it calls.
4. Plan
Decide one spelling per code (the handlers use market_series, the seeds use
series), then seed and grant.
The handlers' spelling wins, because the generated handlers write it from the entity's plural and every other component's codes follow the same rule. So:
- Add
marketdatatoREGISTRYincheck_handler_permissions.pyand let the gate list what is missing. - Seed each code a handler checks, and remove each seeded code no handler checks.
- Map every caller outside the component to the handler it calls, and grant each service role the codes those handlers check.
5. Measurements
Every number this task claims about a tree, with the command that produced it
and the commit it was measured at. A number without both is not a measurement,
because a clean-up record describes a tree and trunk moves under a branch: a
consumer count, a gate's component count and a suite's size all change with no
code changing, and the reader of a bare number cannot tell which tree it came
from. Where the raw output matters, put it in an evidence/ file beside the
task and name that file in the Command column.
| What | Value | Command | Commit |
|---|---|---|---|
| Codes the marketdata handlers check | 17 | check_handler_permissions.required_codes("marketdata") |
c24fb780ff |
| Of those, seeded before this task | 1 (marketdata::observations:read) |
the same, against seeded_codes() |
c24fb780ff |
| Seeded codes no handler checked | 6 (series:read/write/delete, observations:write, fixings:read/write) |
iam_permissions_populate.sql against the 17 |
c24fb780ff |
| Gate after the change | 9 components, 597 codes, all seeded | check_handler_permissions.py |
c24fb780ff |
6. Notes
16 of 17 checked codes are not seeded today. See the 2026-10-02 review, section 4, W02, and
capture CCB1D9D1.
6.1. Who calls a marketdata handler
| Caller | Calls | Code it needs | Grant |
|---|---|---|---|
SyntheticService |
find_series_by_uri, list_observations_page, list_feed_bindings, save_feed_binding |
marketdata::feed_bindings:write; the read handlers check no code |
marketdata::feed_bindings:write only |
Tenant provisioning in ores.iam |
list_feed_bindings, put_feed_binding |
marketdata::feed_bindings:write |
None: it mints a token for the tenant's acting admin, who holds * |
ReportingService |
export_market_data_to_storage |
None: that handler checks no code | None |
| The shell | CRM, curve bootstrap, import, ORE export and entity verbs | the matching code | Through the user's role; TenantAdmin and SuperAdmin hold * |
6.2. Decisions
MarketdataServicekeepsmarketdata::*. Every service role holds its own component's wildcard, and the service is the handler, not a caller of it.marketdata::observations:readstays under its old name. It is the one code the ORE export checks, and it reads every series, observation and fixing; the seed's description now says so.- The six seeded codes no handler checked are removed, and so are
SyntheticService's four grants of them.
6.3. Found here, not fixed here
The handler for marketdata.v1.series.export-to-storage, in
market_series_handler.hpp, checks no code: any authenticated caller can write
every series, observation and fixing of its tenant to object storage. It is the
class of gap capture CCB1D9D1 describes, an operation handler the gate cannot
see checking nothing, and it is recorded there.
7. Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario)
that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
8. PRs
| PR | Title |
|---|---|
| #2594 | [marketdata] Gate and seed the marketdata permissions |
9. Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | PR 2594: a design doc uses the removed marketdata::series:write as its example. |
anatomy_of_a_service.org |
Accepted | The example is marketdata::feed_bindings:write, the code SyntheticService now holds. |
| 2 | PR 2594: make clear that only the export to storage is ungated, not the ORE export. | capture CCB1D9D1 |
Accepted | The capture names the ORE export and the code it checks. |
10. Result
W02 passes for marketdata. marketdata is in the permission gate's REGISTRY,
and every one of the 17 codes its handlers check is seeded under the handlers'
spelling. The six seeded codes no handler checked are gone. SyntheticService
holds only marketdata::feed_bindings:write, the one code its calls need;
tenant provisioning and reporting need none. On a recreated database the fleet
started and 90 of 90 test suites passed. One ungated operation, the export to
storage, is recorded on capture CCB1D9D1.