RuleShop is an online store where the important decisions — discounts, shipping, fraud checks, availability, loyalty points and theming — are not written in code, but configured as rules from an admin control plane. Changing how the store behaves means publishing a new version of the rules. No recompilation, no redeploy.
🥇 Built for OPEN Stage of 2026 National Olympiad of Innovation and Digital Creation, Web section — gold medal (second prize).
The rule engine. A rule is a condition tree (AND / OR / NOT) with
leaves of the form (fact, operator, value) and a list of actions. All six
decision points — pricing, shipping, fraud, availability, loyalty, theme — use
the same engine, the same operators and the same conflict resolution mechanism.
Every evaluation returns the decision along with its explanation: the rules that
matched, the conditions evaluated and the values found.
Control plane. A rule editor with logical groups, operators filtered by fact type, parameterised actions and named priorities. Every rule is automatically translated into natural language, and the testers show the effect before publishing. Includes management of products, orders, customers and shipping methods.
Versioning. Publishing creates an immutable snapshot with a diff and a checksum. Rollback by repointing the active version, a per-category kill switch, deterministic canary cohorts, an evaluation history and an audit log.
Storefront. A catalog with search, filters and sorting, a persistent cart, checkout with simulated payment, guest or account purchase, order history. The customer sees what the price is made of and which rules acted on it.
AI module (Google Gemini). Analyses rules, generates rules from natural
language requirements and classifies fraud incidents. The model does not evaluate
rules, the statistics are computed by the application by re-running the candidate
version over real evaluations, and publishing stays manual. The same functions
are also exposed through a purpose-built MCP server. Details in
docs/AI.md.
Multi-tenant. Several stores served from the same instance, isolated at the level of catalog, rules, orders and customers.
packages/rule-engine/ @ruleshop/rule-engine — the rule engine, no I/O
packages/rate-limit/ @ruleshop/rate-limit — rate limiting (GCRA)
apps/web/ the Next.js app: storefront, control plane, API, MCP
The engine receives a rule snapshot and a fact context and returns the decision
together with its explanation. It does not touch the database and performs no
I/O, so it runs identically in the storefront, in tests and in simulations over
historical events. The application layers are described in
docs/ARCHITECTURE.md.
Node.js ≥ 20.11, pnpm ≥ 10 and Docker are required.
git clone <repo-url> && cd RuleShop
pnpm install
cp apps/web/.env.example apps/web/.env # fill in AUTH_SECRET
docker compose up -d # MongoDB (replica set), Redis, MinIO
pnpm db:push
pnpm db:seed
pnpm create-admin --email admin@ruleshop.dev --store ruleshop-ro
pnpm devThe storefront is at localhost:3000, the control plane
at /admin, with sign-in at /auth/admin. Without --password, the admin
account's password is generated randomly and shown only once.
The seed creates two isolated stores (ruleshop-ro, ruleshop-de), 19 products,
the six rulesets published as version 1, plus 11 customers, 42 orders, fraud
incidents and 280 evaluations in the history. It is deterministic and idempotent.
pnpm test # all tests
pnpm typecheck # type checking across the whole workspace
pnpm build # production build
pnpm mcp # the MCP server (stdio)
pnpm db:studio # inspect the database
pnpm product-art # regenerate the product images
pnpm --filter @ruleshop/rule-engine testVariables go in apps/web/.env (full template in .env.example).
| Variable | Required | Role |
|---|---|---|
DATABASE_URL |
yes | MongoDB; replica set, for transactions |
AUTH_SECRET |
yes | Session signing (openssl rand -base64 32) |
AUTH_URL |
— | The public address of the app |
AUTH_TRUST_HOST |
proxy | Needed behind a reverse proxy |
REDIS_URL |
— | Rate limiting. Without it, limiting stays in process memory |
AUTH_GOOGLE_ID / _SECRET |
— | Customer sign-in with Google |
AUTH_FACEBOOK_ID / _SECRET |
— | Customer sign-in with Facebook |
S3_ENDPOINT, S3_BUCKET, S3_* |
— | Image storage. Without them the disk is used (UPLOAD_DIR) |
GEMINI_API_KEY |
— | Enables the AI module |
GEMINI_MODEL |
— | Defaults to gemini-flash-latest |
MCP_API_TOKEN |
MCP | Service token for the MCP server |
DEFAULT_STORE_SLUG |
— | Development override for the active store |
- The active store is the one served to customers, marked
isDefaultin the database. It is changed from Stores → "Make active", without a redeploy.DEFAULT_STORE_SLUG, as long as it is set, takes precedence. - The administered store is the one opened in the control plane. A
PLATFORM_ADMINcan switch it; aSTORE_ADMINor anOPERATORstays bound to the store on their own account, a restriction enforced on the server.
A store can be turned on or off (Store.active); the active one cannot be turned
off until another is designated. A new store starts with the default shipping
methods and the six rulesets published as version 1.
- Server-side authorization on every admin page, server action and API route, with the role read from the database, not from the token.
- Isolation between stores: every query filters on
storeId, and orders are tied to their owner through an httpOnly cookie or an account, never through the order number. - Validation with Zod at the boundary, plus the engine's semantic validation: operator compatibility with the fact type and parameter bounds.
- Rate limiting on login, checkout, order code lookup, uploads and AI calls,
with the policies grouped in
lib/rate-limit. Login and code lookup reject requests if Redis goes down. - Headers:
nosniff,X-Frame-Options: DENY,Referrer-Policy,Permissions-Policy, noX-Powered-By. - Image uploads validated through magic bytes, with SVG rejected, server-generated keys and serving through a dedicated route.
- Secrets only in environment variables, staff passwords as bcrypt hashes.
A strict CSP is not implemented, and payment is simulated.
| 🛠️ Fix | |
|---|---|
🚫 Transactions are not supported, checkout fails |
The database is not a replica set — start it with docker compose up -d. |
🏪 No store configured |
The demo data is missing: pnpm db:seed. |
| 🔐 Admin sign-in fails with no message | AUTH_SECRET is missing. Behind a reverse proxy AUTH_TRUST_HOST=true is also required. |
| ⛔ "Too many attempts" on login or checkout | Rate limiting. It clears after the interval in the message, or when the ratelimit:* keys are deleted. |
🪟 EPERM ... query_engine-windows.dll.node |
A running pnpm dev holds the file locked: stop it, then pnpm db:generate. |
🤖 Gemini responded with 404 ... no longer available |
Remove GEMINI_MODEL; gemini-flash-latest is used by default. |
| 🔑 The AI features do not appear in the UI | GEMINI_API_KEY is not configured. |
🐢 The first load of a route is slow in dev |
Next compiles the route on first request; this does not happen with pnpm build && pnpm start. |
| 🖼️ Uploaded images do not show up | Check the S3_* variables, or remove them to store on the local disk. |
docs/ARCHITECTURE.md— layers, the rule model, multi-tenancydocs/AI.md— the AI module and the MCP serverpackages/rule-engine/README.md— the rule enginepackages/rate-limit/README.md— rate limiting
