diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 560c942eb..b399df72e 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -18142,7 +18142,8 @@ paths: summary: The signed-in account, and what sharing lets it do description: "Keeps answering while the browser is acting on another record — the switcher, the banner naming whose record is open, and the way back out all read it, so a refusal here would strand a switched session. It is - therefore an ACTOR surface: it always serves the caller's own rows, and a request that attaches + therefore an ACTOR surface: it serves the caller's own rows — apart from `modules` and `cycleTrackingEnabled`, + which answer for the record the session is inside, masked to what the grant opens — and a request that attaches `X-HealthLog-Account` is refused with 403 `sharing.not_permitted` rather than quietly answered. A client that wants a record's data sends the selector on the read that needs it, never on this call." responses: @@ -43886,9 +43887,10 @@ components: description: Fully-resolved module enable/disable map. Every toggleable module key is present. `false` means the surface should disappear end-to-end (nav, dashboard, insights, …). `cycle` mirrors `cycleTrackingEnabled`; `coach` mirrors the resolved per-user opt-out + operator master flag. On `GET /api/auth/me` it answers for the RECORD - the session is inside rather than for the caller, because every surface it gates shows the record's data; with - no switch — which is every native request, since the Bearer transport carries none — the two are the same - account and the payload is unchanged. + the session is inside rather than for the caller, because every surface it gates shows the record's data, and it + is masked to the sections the active grant opens — a module outside them reads `false` rather than the record's + true state. With no switch — which is every native request, since the Bearer transport carries none — the two + are the same account, nothing is masked and the payload is unchanged. PatchModulesResponse: type: object properties: @@ -65765,9 +65767,13 @@ components: - recordSession additionalProperties: {} description: "The signed-in account: its identity, its preferences, and (since v1.36.0) what account sharing lets it do. - Additional properties are the preference fields this spec does not yet enumerate. Under an active switch this - payload still describes the CALLER — their preferences, their modules, their identity — because display - preferences belong to the person at the keyboard rather than to the record they are reading." + Additional properties are the preference fields this spec does not yet enumerate. Under an active switch the + identity and preference fields still describe the CALLER, because display preferences belong to the person at + the keyboard rather than to the record they are reading. Two fields are the exception (v1.38.14): `modules` and + `cycleTrackingEnabled` describe the ACTIVE RECORD, since every surface they gate shows the record's data — and + they are masked to the sections the active grant opens, so a scoped grant reads `false` for a module outside it + rather than the record's true state. With no switch — which is every native request, since the Bearer transport + carries none — the two are the same account and nothing is masked." AccountAccess: type: object properties: diff --git a/src/lib/openapi/routes/account-sharing.ts b/src/lib/openapi/routes/account-sharing.ts index d0d32dad2..07f487963 100644 --- a/src/lib/openapi/routes/account-sharing.ts +++ b/src/lib/openapi/routes/account-sharing.ts @@ -453,7 +453,7 @@ const accountPayload = z .meta({ id: "AccountPayload", description: - "The signed-in account: its identity, its preferences, and (since v1.36.0) what account sharing lets it do. Additional properties are the preference fields this spec does not yet enumerate. Under an active switch this payload still describes the CALLER — their preferences, their modules, their identity — because display preferences belong to the person at the keyboard rather than to the record they are reading.", + "The signed-in account: its identity, its preferences, and (since v1.36.0) what account sharing lets it do. Additional properties are the preference fields this spec does not yet enumerate. Under an active switch the identity and preference fields still describe the CALLER, because display preferences belong to the person at the keyboard rather than to the record they are reading. Two fields are the exception (v1.38.14): `modules` and `cycleTrackingEnabled` describe the ACTIVE RECORD, since every surface they gate shows the record's data — and they are masked to the sections the active grant opens, so a scoped grant reads `false` for a module outside it rather than the record's true state. With no switch — which is every native request, since the Bearer transport carries none — the two are the same account and nothing is masked.", }); /** @@ -475,7 +475,7 @@ export const accountSharingPaths: NonNullable = { tags: ["Auth"], summary: "The signed-in account, and what sharing lets it do", description: - "Keeps answering while the browser is acting on another record — the switcher, the banner naming whose record is open, and the way back out all read it, so a refusal here would strand a switched session. It is therefore an ACTOR surface: it always serves the caller's own rows, and a request that attaches `X-HealthLog-Account` is refused with 403 `sharing.not_permitted` rather than quietly answered. A client that wants a record's data sends the selector on the read that needs it, never on this call.", + "Keeps answering while the browser is acting on another record — the switcher, the banner naming whose record is open, and the way back out all read it, so a refusal here would strand a switched session. It is therefore an ACTOR surface: it serves the caller's own rows — apart from `modules` and `cycleTrackingEnabled`, which answer for the record the session is inside, masked to what the grant opens — and a request that attaches `X-HealthLog-Account` is refused with 403 `sharing.not_permitted` rather than quietly answered. A client that wants a record's data sends the selector on the read that needs it, never on this call.", responses: { ...stdResponses, "200": { diff --git a/src/lib/openapi/routes/profile.ts b/src/lib/openapi/routes/profile.ts index 541cccfc5..90060052e 100644 --- a/src/lib/openapi/routes/profile.ts +++ b/src/lib/openapi/routes/profile.ts @@ -84,7 +84,7 @@ const moduleMapResolved = z .meta({ id: "ModuleMap", description: - "Fully-resolved module enable/disable map. Every toggleable module key is present. `false` means the surface should disappear end-to-end (nav, dashboard, insights, …). `cycle` mirrors `cycleTrackingEnabled`; `coach` mirrors the resolved per-user opt-out + operator master flag. On `GET /api/auth/me` it answers for the RECORD the session is inside rather than for the caller, because every surface it gates shows the record's data; with no switch — which is every native request, since the Bearer transport carries none — the two are the same account and the payload is unchanged.", + "Fully-resolved module enable/disable map. Every toggleable module key is present. `false` means the surface should disappear end-to-end (nav, dashboard, insights, …). `cycle` mirrors `cycleTrackingEnabled`; `coach` mirrors the resolved per-user opt-out + operator master flag. On `GET /api/auth/me` it answers for the RECORD the session is inside rather than for the caller, because every surface it gates shows the record's data, and it is masked to the sections the active grant opens — a module outside them reads `false` rather than the record's true state. With no switch — which is every native request, since the Bearer transport carries none — the two are the same account, nothing is masked and the payload is unchanged.", }); const moduleMapEnvelopeInner = z