Architecture
Accepted architecture · 1 October 2026, option 2
Agentfy hosts the harness; Store Deputy API exposes the single MCP; the OpenCart module exposes a protected REST/JSON API. Store Deputy governs connections, actor/shopper context, permissions, approvals and audit without building another harness. Connect → login/register → secure pairing → automatic agent provisioning in Agentfy with Store Deputy MCP → return to the module. App contains teams, agents, account, billing and per-agent chat.
This replaces the earlier same-day direct-module-MCP design. Existing code and prototypes below do not implement the new integration. Shopper sessions and the real OpenCart cart have separate scoped bindings; cookies stay in the shop. See the accepted central MCP decision for boundaries, delivery stages and remaining contract work.
Status · 1 October 2026. Three of the five parts have code — the API, the app and a first draft of the website — and they run only on a developer's machine. The docs site is published. The admin panel is not built. No real store has been connected: everything described as built was exercised only against two local test shops.
Store Deputy is laid out as five parts in one repository — API, app, admin, website and docs — plus a small module installed in the shop. The founder named the five parts on 1 October 2026. The code follows CleanSlice, the architecture standard this project uses for NestJS and Nuxt applications. CleanSlice covers the API, the app and the admin panel; it does not describe a marketing website, a documentation site or PHP code, and those pages say so.
The parts
| Part | What it is for | Who uses it | Built with | Status |
|---|---|---|---|---|
| API | Knows the people, teams, roles and connected stores. The only part that talks to a shop | The app; later the admin panel and the agents | NestJS, Prisma, PostgreSQL | Foundation built; runs locally |
| App | Where a person signs in and confirms connecting a store | Shop owners and their teams | Nuxt, Vue 3, Pinia, Tailwind | Sign-in, registration and connecting built; runs locally |
| Admin | A panel for whoever operates Store Deputy itself | Platform operators | Nuxt, if built as CleanSlice describes | Not built; scope undecided |
| Website | The marketing site: what a shop owner reads before deciding to try the product | Buyers | Nuxt, Vue 3, Tailwind | First draft built: one page; runs locally |
| Docs | Internal: research, decisions, specifications and design prototypes | The team and the agents working on the product | VitePress | Published |
| Shop module | Installed in OpenCart. Carries out signed requests from the API and gives the owner Connect, Pause and Disconnect | The shop owner; the API | PHP 7.3+, OpenCart 3.0.3.5+ | Connecting built; run on local test shops only |
The shop module is specified in REST 1.0 and is not repeated here.
How the parts talk
buyer ─────▶ website marketing page; links to the app
owner ─────▶ app ──────┐ sign in, register, connect a store
operator ──▶ admin ────┤ not built
▼
API ─────── PostgreSQL
│
│ Bridge Protocol v1: signed requests
▼
module in the shop (OpenCart 3)- The browser parts talk only to the API. The app's API client is generated from the API's own description of its routes, not written by hand.
- Only the API talks to a shop. It does so through REST 1.0. The app never calls a shop.
- Connecting a store crosses three parts. The owner presses Connect in the shop module and lands in the app with a one-time code. The app asks the API to connect. The API sends a signed request to the module, and the owner is returned to the module page they started from.
What CleanSlice asks of the code
CleanSlice combines two ideas. Code is grouped by feature, and inside each feature it is separated by responsibility.
- A slice is one feature in one folder. Everything a feature needs lives together. Adding a feature means adding a folder; understanding one means reading one folder.
- Three layers inside a slice. Presentation accepts input and shapes output: controllers and request shapes in the API, pages and components in a browser app. Domain holds the rules and the contracts for anything outside. Data fulfils those contracts by talking to the database or to another service.
- Rules never depend on plumbing. The domain layer states what it needs as a contract, called a gateway. The data layer implements it. A rule such as "only an owner or an admin may connect a store" can be read and tested without a database or a network.
- Setup slices and feature slices. Setup slices carry shared plumbing: the database connection, the error format, the theme, the API client. Feature slices carry the product.
- Related slices share a namespace folder. Sign-in, teams and membership sit together under
user/. The parent folder holds no code of its own. - Slices meet through their domain. One slice may use another's rules and types, never its data layer.
- Names. Slice folders are singular (
user,store); routes are plural (/teams). - A fixed stack. NestJS and Prisma for the API; Nuxt, Vue 3, Pinia and Tailwind for browser apps.
- Four agreed phases. High-level plan, detailed plan, implementation, review — each agreed before the next, and the API before the app.
When sources disagree, the order in this repository is: CleanSlice, then the repository's agent guide, then existing code.
Where this repository departs from CleanSlice
| CleanSlice | Here | Why |
|---|---|---|
The prisma-import package merges each slice's database schema | A small script of our own merges them | Recorded reason: the package was last published in February 2024 for Prisma 4 |
| An interceptor formats errors | A global exception filter formats them | An interceptor never sees an error thrown by the sign-in check, so "not signed in" would come back in a different shape |
| Its NestJS standard shows a controller calling a gateway; its layers page shows a controller calling a service | Controller → service → gateway everywhere | Role rules are business rules and belong in a service |
| The reference app includes a dependency-injection setup slice | The app has none | Not needed yet. CleanSlice itself reserves it for slices that need mock or offline implementations |
| UI components generated by shadcn-vue | Five hand-written components in the same style | Decided 29 September 2026. Return to shadcn-vue when dialogs, menus or tables are needed |
Every component folder has a Provider.vue | The app's shared page-header folder has none | Observed on 1 October 2026; not a recorded decision |
Local addresses
Everything below is a developer's machine. The product has no public address.
| Part | Port | Note |
|---|---|---|
| App | 4140 | |
| Admin | 4141 | Reserved; nothing listens |
| API | 4142 | Interactive route description at /api |
| Test shops | 4143, 4144 | OpenCart 3.0.3.5 and 3.0.5.1 |
| API database | 4145 | PostgreSQL in Docker |
| Docs | 4146 | Preview of a production build on 4147 |
| Local dashboard | 4152 | Links, service status and logs for a developer; not part of the product |
| Website | 4148 |
Read next
- API — the slices that exist, and one request followed through the layers.
- App — what a person can do today and how the app is put together.
- Admin — what is reserved and what must be decided before it is built.
- Website — the marketing site: what is on it, how it is arranged and what to decide next.
- Docs — how this site is arranged and why CleanSlice does not govern it.
- Implementation proposal — the target design for catalog work, approvals and jobs, none of which is built.