Skip to content

feat(cli): honour trustedProducts from the local product config - #702

Merged
filvecchiato merged 2 commits into
rfc/granted-scopesfrom
feat/cli-manifest-grant-testing
Sep 10, 2026
Merged

feat(cli): honour trustedProducts from the local product config#702
filvecchiato merged 2 commits into
rfc/granted-scopesfrom
feat/cli-manifest-grant-testing

Conversation

@filvecchiato

Copy link
Copy Markdown
Collaborator

Closes #524. Based on rfc/granted-scopes (#454), not main — the manifest cache it seeds only exists there.

What

truapi-host --product-config <path> reads the LocalProductConfig the publisher reads (docs/rfcs/product-manifest.md), takes its trustedProducts, and seeds the manifest cache the core would otherwise fill from dotNS. Repeat the flag to serve a grant between two products.

Against #524's requirements:

  • Reads trustedProducts from the local manifest config and applies it for the run. Same field, same shape, same file the publisher will read.
  • Cross-product flows work between two locally served products. Covered by the tests below.
  • A local grant is visible as such. The host logs what it applied under "declared in config, not published to dotNS", every run.
  • Development relaxation or faithful local implementation? Faithful, and the module doc says so. The core resolves the grant through the code it always runs; a scope it does not honour is refused here exactly as on chain. Only the document's origin differs. Nothing bypasses manifest_grants_scope.

Tests

peopl.paseo grants dim2.paseo the storage scope; dim2.paseo reads a value peopl.paseo wrote. The read goes through real wire frames into a SigningHostRuntime on the real CliPlatform — which is the half a stub cannot cover. A host that keys storage off the product that asked, rather than the owner encoded in the key, returns nothing and the grant silently does nothing.

Also covered: an ungranted product refused, and a product with no config refused identically, so the local path keeps the uniform refusal the RFC requires.

New public API

One function on truapi-server, encode_cached_root_manifest. The alternative was hand-encoding the cache's SCALE shape in the CLI, which #454 just changed once already.

Not addressed

The grant expires with the cache lifetime. A host outliving it starts refusing grants the developer can still see in their own config; the doc on apply says callers should re-seed on the same period, and nothing does that yet.

A product under development has nothing on chain, so every cross-product call
is refused and the flows a partner integration exists for cannot be exercised
at all. `truapi-host --product-config` reads the same file the publisher reads,
takes the trustedProducts a developer already wrote there, and seeds it into
the manifest cache the core fills from dotNS.

This is the manifest path, not a way around it: the core resolves the grant
through the code it always runs, and a scope it does not honour is refused here
exactly as it would be on chain. Only the document's origin differs, so the
host prints what it applied and says it was never published.

Repeating the flag serves a grant between two products, which is what the tests
do: peopl.paseo grants dim2.paseo the storage scope, dim2.paseo reads a value
peopl.paseo wrote, and the read goes through wire frames against the real CLI
platform. That last part is the half a stub cannot cover — a host keying
storage off the product that asked rather than the owner in the key returns
nothing, and the grant silently does nothing.
`make e2e-cross-product-storage` runs five phases against the real binary,
driving the @parity/truapi client over the frame protocol. peopl.paseo writes
its own storage; dim2.paseo reads it because peopl.paseo's product config names
it; stash.paseo reads the same key and is refused; a product that published
nothing is refused the same way, so the call cannot be used to find out which
products exist; and dim2.paseo reads once more at the end, so a refusal in the
middle cannot be the value having gone.

Each phase is its own host process. They share a base path, so the value a read
returns was written by a process that has already exited, which is what makes
this cross-product rather than cross-connection.

The fixtures are ordinary local product configs, so they double as an example
of the file. dim2.paseo's grants nothing: grants point inward, and reading
peopl.paseo needs only peopl.paseo's config.
@filvecchiato

Copy link
Copy Markdown
Collaborator Author

@lore-bot-app review

@filvecchiato
filvecchiato merged commit b11f84e into rfc/granted-scopes Sep 10, 2026
19 checks passed
@filvecchiato
filvecchiato deleted the feat/cli-manifest-grant-testing branch September 10, 2026 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

truapi-host: honour manifest-declared cross-product trust for local development

1 participant