Running it in a regulated shop
This page says what the packages do, so you can answer what a privacy or consumer-law review asks. It isn't legal advice. Which rules apply depends on where you sell.
What leaves your server
Nothing, unless you pass a provider, or a cache whose store runs somewhere else.
With a provider, What the model sees lists what each call carries in each mode. A call never names the shopper, but it can still be personal data:
- The category the shopper leans towards most, and whether they have any history, come from their own data.
- The segment goes exactly as you wrote it.
- If you picked or ordered the candidates for this shopper, they're about them too.
- In per-shopper mode, the call also carries the shopper's own signals.
The provider should be your processor, under a contract that says so. For Anthropic, see Where the calls go.
A shopper who objects to profiling
Leave signals and user.segment out of their page view. In per-shopper mode, also leave out context.searchQuery and user.isReturning. They then get the block for a shopper with no history, built from the page and your catalog.
To skip the model and the cache as well, call generateDeterministic with that page view:
import { createAnthropicProvider } from '@rudra-js/anthropic';
import { createComponentGenerator, type TrackingInputDraft } from '@rudra-js/core';
const generator = createComponentGenerator({
provider: createAnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY! }),
});
export async function recommendations(pageView: TrackingInputDraft, allowsProfiling: boolean) {
if (allowsProfiling) return generator.generate(pageView);
return generator.generateDeterministic({
user: { id: pageView.user.id },
context: pageView.context,
candidates: pageView.candidates,
bundles: pageView.bundles,
});
}- The page view keeps only the shopper's id, the page, the candidates and the bundles.
generateDeterministicstill personalises when you pass it signals, so leave them out.
Deleting a shopper's data
The packages keep nothing about a shopper except what's in the cache.
In the default mode, a cache entry holds no shopper id, and every page view in its cohort shares it. An entry is about one shopper only when you picked its segment or its candidates for that shopper. In per-shopper mode, every entry is about one shopper.
Nothing maps a shopper to their entries. So keep the cache's ttlMs short, or pass createNullSpecCache().
onEvent sends no shopper id. In per-shopper mode its key is made from one. Its violations can name products the shopper bought, disliked or has in the basket. Treat logs of events as personal data.
Caching pages
A page with the block on it can say “Goes with your cart” or “You looked at this recently” in its HTML. Serve those pages with Cache-Control: private or no-store, so a shared cache never hands one shopper's page to another.
Telling shoppers a model wrote the copy
The block's wrapper carries data-rudra-source. It says llm or cache when a model wrote the copy, and fallback when it didn't. You can build on it, for example to show a note.
Whether you must label copy a model wrote, and how, depends on where you sell. In the EU, Article 50 of the AI Act is the rule to ask about.
Claims in the copy
The copy on your page is your own advertising, whoever wrote it. Core's checks catch prices, discounts, stock, ratings, delivery, urgency and generic environmental claims. They work from lists, so a reworded claim gets through. Anything they miss is still yours to stand behind. Checking the wording yourself lists what the number check and the word list miss.
Children
In the UK, the ICO's Children's Code covers any shop that under-18s are more likely than not to use. That includes shops not aimed at them. Its standard 12 asks for profiling to be off by default, unless you can show a compelling reason in the child's best interests. When profiling is on, it asks for safeguards.
For a shop like that:
- Don't use per-shopper mode.
- For a shopper under 18, leave
signalsanduser.segmentout of their page view, unless they've switched personalisation on. Do it the same way as for a shopper who objects. - If you can't tell a shopper's age with a confidence that fits the risk, treat every shopper as under 18.
The default mode alone doesn't turn profiling off. With the default rank: 'signals', core still orders products by the shopper's signals, even with no provider. The default mode also sends the model their strongest category, whether they have any history, and any segment.
Marketplaces
If other sellers list products on your site, Article 27 of the EU's Digital Services Act applies, unless you're a micro or small business. It asks you to set out, in your terms, your recommender's main parameters and why they weigh as they do. It also asks you to describe any options shoppers have to change them. If shoppers can pick between options, such as switching personalisation off, the control has to sit where the products are shown.
The parameters depend on the mode and on rank:
- In the default mode, and in the block core builds without a model,
ranksets the order. Withrank: 'given', that's the order you sent, so describe your own ranking. - With
rank: 'signals', the order comes from the shopper's interest in each category, how often they've viewed the product, its rating, and the tags it shares with products they've engaged with. Category weighs most. The category they're browsing counts towards their interest. - In per-shopper mode, the model picks which candidates to show and in what order, from what it sees.
- In the default mode, the model picks the hero's product from the candidates.
- In every mode, core leaves out what the shopper can't be shown.
bundleSchemasays how core picks a bundle.
Accessibility
Every card is one link with a readable name. Headings only render when they have text. Contrast, focus styles and layout come from your stylesheet, so those are yours to check.