From 36a4631eacda07a452be769962ffae0dd3ac4592 Mon Sep 17 00:00:00 2001 From: Perki Date: Tue, 19 May 2026 17:16:58 +0200 Subject: [PATCH] =?UTF-8?q?fix(cmc):=20readOffer=20must=20omit=20`streams`?= =?UTF-8?q?=20filter=20=E2=80=94=20:=5Fcmc:=5Finternal:offer=20is=20not=20?= =?UTF-8?q?a=20real=20parent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the previous `streamIds → streams` fix, readOffer still fails against any real api-server with `unknown-referenced-resource`: the api-server validates that streams referenced in events.get actually exist, and `:_cmc:_internal:offer` is NOT auto-provisioned on every user account — only the per-capability children `:_cmc:_internal:offer:` are minted by capabilityMintHook. The accepter doesn't know from the capabilityUrl alone, so a specific-stream filter isn't possible either. The plugin's own implementation of the same read (`readOfferViaCapability` in acceptOrchestration.ts:71-78) handles this correctly: omit the streams filter entirely and rely on the capability access's permissions to narrow the response to the single offer event this token can read. Mirror that approach. Keep the `types: [ET_REQUEST]` filter as defense in case future plugin revisions place additional event types on the offer stream. Verified end-to-end on demo: readOffer now returns the offer with `requestedPermissions`, `consent`, etc. No new tests in this PR — the existing cmc.test.js mocks Connection.apiOne, so neither the field-name bug nor this parent- stream-doesn't-exist bug is caught at unit-test time. Both surface only against a real api-server. --- components/pryv-cmc/src/index.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/components/pryv-cmc/src/index.js b/components/pryv-cmc/src/index.js index bc6ca18..7db1976 100644 --- a/components/pryv-cmc/src/index.js +++ b/components/pryv-cmc/src/index.js @@ -550,12 +550,20 @@ async function requestScopeUpdate (conn, params) { async function readOffer (capabilityUrl, opts) { const pryv = (opts && opts.pryv) || require('pryv'); const cap = new pryv.Connection(capabilityUrl); - // `events.get` takes `streams` (recursive read filter), not `streamIds` - // (which is the events.create write target). Mirror the field used by - // the other events.get callers in this file (listInvites, waitForAccept, - // listAcceptedRelationships). + // The capability access has `read` on a single per-capability stream + // (`:_cmc:_internal:offer:`) but the accepter doesn't know + // from the capabilityUrl alone. The parent `:_cmc:_internal:offer` + // is NOT a reserved stream that auto-exists on every user account — only + // the per-capability children do — so a `streams: [':_cmc:_internal:offer']` + // filter resolves to `unknown-referenced-resource`. + // + // Mirror the plugin's own readOfferViaCapability (acceptOrchestration.ts): + // omit the streams filter entirely and rely on the cap access's + // permissions to narrow the response to the single offer event this + // token can read. The `types` filter is defensive in case the offer + // stream ever holds more than one event in future revisions. const events = await cap.apiOne('events.get', { - streams: [NS_INTERNAL + ':offer'], + types: [ET_REQUEST], limit: 1 }, 'events'); if (events.length === 0) {