Summary
IAuthorizer::authorize's doc says it is "Called once per execute envelope". RemoteServer also calls it on instances and schemas, passing an empty actionType. An authorizer written as an allowlist over actionType — the shape the parameter doc invites — therefore evaluates against "" on the two disclosure channels.
Verification status
Verified by reading the call sites. Not exercised against a real IAuthorizer implementation; I have not shown a concrete policy failing open.
grep -n "_authorizer->authorize" include/morph/core/remote.hpp:
1192: if (!_authorizer->authorize(env.session, env.typeId, {})) { <- instances
1209: if (!_authorizer->authorize(env.session, env.typeId, {})) { <- schemas
1317: if (!_authorizer->authorize(env.session, env.modelType, env.actionType)) { <- execute
The server side is deliberate and says so (remote.hpp:1188-1191, :1201-1207: "gate it with authorize for the model type (empty action id)"). The interface doc is the stale side.
Why it matters more than a wording nit
session.hpp:111 documents @param actionType as "String id of the action being invoked", with no note that it can be empty. An implementation that switches or matches on actionType will hit its default arm for instances and schemas — and schemas returns, per remote.hpp:1204-1206, "field names, bounds, rules and the payload fingerprint of every action". Whether that fails open or closed depends entirely on how the embedder wrote the default, and nothing in the contract tells them the case exists.
SigningAuthorizer::Policy (session_auth.hpp:521-526) has the same gap: its doc says only "the target ids".
docs/spec/session/session.md:416 repeats the stale claim ("Called per execute envelope"), so header and spec agree with each other and disagree with the code.
What would change the verdict
- Close it by correcting the three doc sites — that is very likely the whole fix; the server behaviour looks correct and intentional.
- Raise it if any shipped or example authorizer is shown to fail open on the empty
actionType.
Adjacent, same file
SigningAuthorizer runs a full token verification twice per request — authorize() (session_auth.hpp:572) and authenticate() (:583) each call _verifier.verify(ctx.token, _clock()). That is two base64url decodes, two HMAC-SHA256 passes and two Glaze parses per authenticated request, from two separate clock samples. remote.hpp:1323-1330 documents the resulting expiry window; none of the three doc blocks in session_auth.hpp mention that verification is repeated or that the two calls can disagree. Also session_auth.hpp:470 builds std::string{payload} for a string_view parameter, allocating on every verification — so twice per request.
Summary
IAuthorizer::authorize's doc says it is "Called once perexecuteenvelope".RemoteServeralso calls it oninstancesandschemas, passing an emptyactionType. An authorizer written as an allowlist overactionType— the shape the parameter doc invites — therefore evaluates against""on the two disclosure channels.Verification status
Verified by reading the call sites. Not exercised against a real
IAuthorizerimplementation; I have not shown a concrete policy failing open.grep -n "_authorizer->authorize" include/morph/core/remote.hpp:The server side is deliberate and says so (
remote.hpp:1188-1191,:1201-1207: "gate it withauthorizefor the model type (empty action id)"). The interface doc is the stale side.Why it matters more than a wording nit
session.hpp:111documents@param actionTypeas "String id of the action being invoked", with no note that it can be empty. An implementation that switches or matches onactionTypewill hit its default arm forinstancesandschemas— andschemasreturns, perremote.hpp:1204-1206, "field names, bounds, rules and the payload fingerprint of every action". Whether that fails open or closed depends entirely on how the embedder wrote the default, and nothing in the contract tells them the case exists.SigningAuthorizer::Policy(session_auth.hpp:521-526) has the same gap: its doc says only "the target ids".docs/spec/session/session.md:416repeats the stale claim ("Called perexecuteenvelope"), so header and spec agree with each other and disagree with the code.What would change the verdict
actionType.Adjacent, same file
SigningAuthorizerruns a full token verification twice per request —authorize()(session_auth.hpp:572) andauthenticate()(:583) each call_verifier.verify(ctx.token, _clock()). That is two base64url decodes, two HMAC-SHA256 passes and two Glaze parses per authenticated request, from two separate clock samples.remote.hpp:1323-1330documents the resulting expiry window; none of the three doc blocks insession_auth.hppmention that verification is repeated or that the two calls can disagree. Alsosession_auth.hpp:470buildsstd::string{payload}for astring_viewparameter, allocating on every verification — so twice per request.