Vendune
docs/architecture.mdView on GitHub ↗

See v0.5 connected intelligence, app and payment paths for the current implementation and performance limits.

Architecture decisions and next boundaries

See the SaaS scalability roadmap for code-specific bottlenecks, cell architecture, delivery order and proposed capacity/economics tests.

One transactional core, independently deployable workers

Fresh installations use Rust/Axum, ordinary PostgreSQL 17 and private Qdrant v1.16.3. PostgreSQL owns commerce records, knowledge relations, source vectors and the durable synchronization queue; Qdrant supplies rebuildable vector search. Candidates are hydrated and checked against current PostgreSQL state. AGE and pgvector are retained for legacy conversion, not required for a fresh install. See managed hosting for migration and index-consistency boundaries. The runtime can separately deploy HTTP, memory/outbox, payment, app-event, translation and media roles. HTTP-only deployments need workers for queued work. SQL leases and receipts coordinate their work. Inference still uses an external Ollama/provider service and process-local admission. Prices, reservations and order placement share a database transaction initially.

Catalog operations use a pool of 20 connections. Checkout locks the cart and its product rows in deterministic ID order; an advisory lock serializes one tenant/idempotency key across concurrent requests. Disjoint product orders can progress independently. There is no unbounded model call in this transaction. All writes originate from deterministic operations, not model-generated SQL.

The illustrated production architecture explains implemented money, database security, inventory, payment, cache, admission and observability foundations. Orders/provider boundaries now use explicit minor integers and currency scale; the ported calculators deliberately retain Shopware float behavior and remain behind the original-source differential gates. Core FORCE RLS requires a separate non-owner runtime login and strict startup; direct or session pooling is supported. All payment methods have persisted allocations, provider money states have central admission, committed outbox events evict settings caches, and replicas share daily interactive HTTP AI quotas.

Remaining production steps include integer pricing/tax intermediate rounding, distributed concurrent/spend/job quotas, telemetry export, independently operated cells and measured recovery/failover. The guide gives exact configuration, tests, privileged exceptions and limits rather than treating the foundation as complete production certification.

Intelligence is connected to state

The model reads tenant-scoped product snapshots and experience state. Its output is parsed into typed changes. The server binds actual revisions to the snapshot, persists the proposal, and the merchant approves exactly that stored proposal. Applying it is atomic and rejects stale revisions. Browser, MCP and merchant endpoints call the same operations; agent output has no separate privileged execution path. The proposal summary is model text, not a guarantee that it matches the changes: the UI displays the actual fields for review.

Customer advice selects catalog IDs and one registered layout. Local behavior updates ordering immediately while keeping keyed product/cart/form components stable. A small policy stores which variant was exposed, its selection probability and a reward generated by a simulated order. Model weights remain fixed. Production experiments need randomized control, delayed outcome, refund corrections, trust/consent and contribution-margin objectives.

Extension isolation

The guest signature is approve(total_minor: i64, limit_minor: i64) -> i32. Only merchant-authorized modules are activated. The engine compiles at activation; instances get 10,000 fuel units, 1 MiB memory, a 256 KiB Wasm stack and no host imports. Source is limited to 32 KiB. Infinite loops and oversized memory are tested. Source/digest survive restart; boot recompiles saved modules.

This is a genuine small sandbox, not a general PHP JIT. More powerful hooks need versioned typed host capabilities and a separate quota/sandbox worker. Activation caches are process-local and refresh from the persisted policy inside checkout; the two-instance extension regression verifies this boundary. Compilation also needs stronger process-level time/memory limits for untrusted SaaS authors.

Standards are adapters

The capability registry describes operations and typed MCP inputs. UCP checkout calls the same cart and checkout implementation. Protocol versions and the unimplemented portions are documented rather than claiming all agentic standards are one interchangeable interface.

Primary protocol sources checked during implementation:

  • https://ucp.dev/specification/shopping/checkout/rest/
  • https://ucp.dev/2026-08-25/schemas/shopping/checkout.json
  • https://modelcontextprotocol.io/specification/2026-07-28/server/tools
  • https://modelcontextprotocol.io/specification/2025-11-25/basic/transports

Historical v0.2: one data engine, three representations

This section records the original AGE/pgvector implementation. The current PostgreSQL/Qdrant replacement is described above; the extension requirements and exact-vector search details below apply to the earlier version.

PostgreSQL owns orders, stock, revision checks, outbox, conversations and proposals. Apache AGE owns explicit Product/Need nodes and SERVES/PAIRS_WITH relationships. pgvector stores real 1,024-dimensional Qwen embeddings with model and document hashes. All three are in the same open database, backed by the existing volume. Startup migrations and initial graph construction are serialized with a database advisory lock.

Graph queries are fixed code templates with bound agtype parameters. Product metadata updates join merchant approval's transaction. The seeded relations have curated-demo provenance; neither their presence nor embeddings imply learned causal knowledge. Search filters by tenant and model before exact ranking, then joins current price/stock/revision from the ledger. Unchanged documents reuse embeddings. No ANN index, graph sharding or million-product performance is claimed. A production system needs bounded graph neighborhoods, tenant partitioning and retrieval before model context construction; the six-product demo still passes the full bounded catalog to the planner.

Historical v0.2: persisted conversations and provider boundaries

The transaction-held inference described here was later replaced with short conversation leases and network calls outside database transactions; see the current inference path.

The merchant chat reads current catalog, graph, experience state, recent conversation and verified simulated-order aggregates. Ollama (explicitly disabling optional thinking for bounded structured operations), OpenAI Responses and Anthropic Messages produce the same typed proposal. Optional cloud keys remain on the server; provider errors create explicit chat messages without executable proposals. There is no silent fallback.

Conversation turns use a per-conversation advisory try-lock and retain the last 16 messages as model context. Inference holds a conversation transaction, not stock/product locks. It is an asynchronous HTTP request inside one process, not a durable worker; long-running jobs should move to a worker with bounded queues and cancellation before scaling. Approvals remain a separate short transaction. History reads join the actual task's applied flag so reloading cannot offer an already-applied change as pending.

The MCP stdio bridge forwards to Rust's shared capabilities. Remote ChatGPT or Claude account registration, HTTPS deployment and OAuth remain deployment work; the prototype does not imply local endpoints are reachable from hosted clients. Cold restart checks include graph relations, exact stored vector digests, conversations and approval state, alongside the earlier ledger/policy checks.

Historical v0.3: merchant visibility and language context

Vendune Studio reads one tenant-scoped overview containing actual order aggregates, seven-day counts, inventory, AGE relations, vector-index metadata, recorded policy counters, activity and provider/connector boundaries. Every product selection shares the same preview state. Preview quote construction is in-memory and executes the real deterministic pricing operation without writing a cart, order or inventory. Mobile users open the same preview in a native dialog. Locale/theme preferences persist; the merchant token does not.

The planner now receives recorded policy views/rewards and diagnostic channel counts as verified facts. Its stored evidence preserves those input facts with the chosen response locale. Graph facts are explicitly curated and the policy learns an observed reward association, not causal uplift or new model weights. The history groups exposures by initial session time and displays their current reward state; it does not invent historical estimates. New approvals record an actual applied timestamp; old proposals retain only their creation history.

HTTP call counters are best-effort asynchronous diagnostics for Store API, MCP and UCP. They count requests, including tests; no external platform or customer attribution is inferred. A cart's initial adapter is stored with its order; this identifies the entry path, not the identity of a hosted agent.

The original Shopware language-chain slice feeds native translated field hydration. English, German, French and Spanish products are seeded once; Swiss German demonstrates parent fallback. Currency stays EUR. Original rule priority and calculated-tier selection replace the earlier hardcoded quantity branch, and normalized quantities are persisted in real carts. This remains a bounded context/product-cart port with its own original-PHP differential gate.

Current native automation and hands-on setup

automation_rules/ evaluates reviewed source-named conditions. The marketing context builders supply private server-owned facts; referenced rules are fetched by tenant/ID and frozen per matching event. marketing/pipeline_runtime.rs executes an admitted graph using durable node receipts, cursors and scheduled continuations. A current membership check precedes each action. Internal product metadata is excluded from public serialization. Apps share the existing typed action gateway; AI proposals retain merchant review.

This is an executable native subset, not a certified translation of the full Shopware interpreter, DAL, trigger set or FlowSequence protocol. See automation.md for source/configuration boundaries. The playground creates a separate shop through personal-owner APIs and seeds repeatable provider-free configurations; it does not bypass production handlers or add a second business-logic implementation.

The standard synthetic catalog lives in src/demo_catalog.rs and fixtures/fashion-catalog.json. New shops receive the Nord Atelier fashion template when demo catalog seeding is requested; scripts/fashion_demo.py tests the actual signup, variants, translations, images, isolated checkout and process restart. See Fashion demo.