Features to add, functions to build, and the prod server prep
Features to add, functions to build, and the prod server prep
Young's rulings carried: Oracle ARM 4 vCPU · 24 GB · 200 GB (this changes my earlier estimate — see sizing below) · POS payments + offline are gaps to extend · EVE beta, "we will make it work for our business" · prices in AUD.
Part 1 — What we already have vs what must be built
Tam's studies are the inventory. Nothing below is invented; each row names its source.
| Capability | State | Source |
|---|---|---|
| Medusa backend (server + worker, Postgres, Redis) | exists — compose + shared image pipeline specced | hosting-docker-study |
Next.js storefront, useEveAgent() chat UI | exists (pattern verified in EVE docs) | eve-integration-study |
Per-tenant EVE agent: instructions.md · tools/ · connections/medusa.ts · skills/ | exists | eve-integration-study |
| HIL workflow gating (refund, bulk-pricing) | exists as design | eve-integration-study |
| POS: staff auth, setup wizard, barcode scan, cart → draft order, order history | exists in the Agilo fork | nextjs-commerce-study |
| POS: unified stock via sales-channel + inventory modules | exists | nextjs-commerce-study |
| POS payments | GAP — must build | nextjs-commerce-study §MVP gaps |
| POS offline mode | GAP must extend (cached browse only today) | nextjs-commerce-study §MVP gaps |
| Tenant provisioning + tenant-key-or-metered AI overage | GAP — new (Mira's pricing guardrail) | business-plan |
Total: 3 build items, and they are exactly the ones Young named. Everything else is assembly.
Part 2 — Feature spec: POS payments
The constraint: the Agilo POS talks to Medusa's Admin REST API over HTTPS only — no backend changes. So a payment path that FITS is needed, not a rebuilt POS.
Design: a Medusa Payment Provider, not POS surgery.
A Medusa payment module (medusa-payment-*) is a swappable provider slot — the commerce module layer integrates providers exactly as Stripe is integrated today. The POS cart already creates draft orders; adding a payment session and capturing against it is a provider module, and the POS needs only to mark the draft paid.
Scope:
- Payment provider module (AU-first: Stripe, later a second processor). Cart → draft order → payment session → capture. Draft-order payment is the extension point — it already exists in Medusa.
- Cash + EFTPOS manual path — shipped FIRST, deliberately. Agilo has "no integrated payment processing yet"; the honest MVP is tendered amount recorded against the draft order, settled outside the system, reconciled by a daily totals job. This is sellable day one and needs no gateway contract.
- Card, second phase. Stripe domestic; terminal integration (the Agilo roadmap item) is phase 3 and needs our own hardware decision.
- Reconciliation function (the part that makes it trustworthy): daily job comparing Medusa order totals vs payment captures, reporting per tenant. Without this the gap just moves from "no payments" to "unknown payments".
Aud note: Mira's plan passes card fees through (Stripe 2.9% + $0.30) at AUD pricing — so the provider must expose processor fees per transaction in the reconciliation, not buried in a payout.
Part 3 — Feature spec: POS offline mode
The constraint: Agilo is cached browse only today, and the Admin API is HTTPS-only (no API reachable without network). Offline cannot mean "full POS offline" — it means degraded POS that never loses a sale.
Design: an offline queue with a deliberate ceiling.
- What works offline: scanning, carting, tender recording, and a held-sale receipt with a local receipt number.
- What syncs: a queue in local storage (SQLite on device), replayed when the network returns, in order, idempotent on the local receipt number (an idempotency key is the difference between offline-sync and double-charging).
- Stock, honestly: reads come from the last successful cache; stock is marked stale in the UI with the cache timestamp. A till must not promise availability it can't check — that is a support call, not a feature.
- The ceiling we ship with: offline supports held sales only — they sync and complete when the network returns. It does not attempt offline payment capture. A card capture requires the network; claiming offline card payments would be a false feature.
Why a ceiling is a design decision: the failure mode here is a shop taking cash all weekend on a cached catalogue, syncing on Monday, and discovering Saturday's stock sold twice. Marking stock stale and holding sales is what makes that impossible.
Part 4 — Functions to build (engineering backlog)
| # | Function | Priority |
|---|---|---|
| F1 | Tenant provisioning: compose → seed → keys → EVE agent config → health check | first |
| F2 | Tenant backup + restore drill, per tenant, DR tested not declared | high |
| F3 | AI conversation metering + per-tenant overage switch (Mira's guardrail) | high |
| F4 | Manual payment path + daily reconciliation job | high |
| F5 | Offline queue + idempotent replay in Agilo | medium |
| F6 | EVE version pin per tenant + upgrade path | high (EVE is beta) |
| F7 | HIL gate as a provable control — provisioning must be able to show it is armed | high |
| F8 | Alerting: per-tenant health (/health + /eve/v1/health), failed pods, stale stock | medium |
| F9 | Connection-pooling + resource ceiling per tenant compose (noisy neighbour) | medium |
F7 is the one I will not compromise. HIL gating is what keeps an AI-staff tier sellable — a convention here is V2's built-never-wired defect all over again. It gets a control, a probe, and a MUST-FAIL test, or it does not ship.
Part 4b — Security functions (Echo, 2026-10-18)
The three-layer identity model ([2fa-and-account-management](2fa-and-account-management),
[authelia-optimization](authelia-optimization)) adds these to the backlog. All A$0 software — the
work is wiring, not licences.
| # | Function | Priority |
|---|---|---|
| S1 | Enable Medusa core MFA — set AUTH_MFA_ENCRYPTION_KEY per tenant, disable_policy: challenge | first (one env var) |
| S2 | requireMfa: {maxAgeSeconds} on elevated writes — refunds, bulk-pricing (strengthens F7) | high |
| S3 | Provisioning creates an invite, not a user — Medusa has no "create user with password" | high |
| S4 | Authelia L1 gate: tenant private KB / staging portals | high (Ace, ~30 min) |
| S5 | Do NOT forward-auth the tenant Medusa Admin — avoids a double MFA prompt | rule, not a task |
| S6 | 2FA enrolment + recovery-code UX (Nova's lane) | medium |
Two things this spec must NOT promise
- Staff roles / permissions. RBAC is a paid Medusa Enterprise feature,
not MIT core — Medusa's
ENTERPRISE-LICENSE.mdlists the RBAC module, its admin APIs and its dashboard routes as proprietary. A tenant needing role separation is a licence line item for Mira, not a build task. The pilot ships with all admin users as full admins. - A confirmed audit log. The 2FA doc asserted "Audit log (Medusa built-in)"; I could not find that feature in the MIT docs. Treat as unverified until someone locates the page. Authelia gives audit for L1 only.
One correction to Part 5 item 5
The firewall note says the current 8442–8448 / 9903–9906 listeners "are
LAB-style loopback and must not be reachable from the public interface". That is
right — but I verified on prod that no SMTP listener exists either
(25/587/465 all closed), which matters because Medusa's default notification
provider is Local: it logs the email and sends nothing. A tenant left on the
default completes orders and silently sends no confirmation ever.
Add to the prep list: an email-delivery probe in the onboarding gate.
/health and /eve/v1/health both pass while email is dead — the failure is
invisible by construction. (Provider choice is Sol's; see
[medusa-cloud-engineering-verification](medusa-cloud-engineering-verification) §2.)
Part 5 — Production server prep
Measured on 161.33.71.67 (Young's node), not assumed:
| Surface | Measured |
|---|---|
| CPU / arch | 4 vCPU, aarch64 — ARM64 confirmed, so every image must be linux/arm64 |
| Memory | 23 Gi total, ~21 Gi available |
| Disk | 193 GB, 14 GB used, 179 GB free |
| OS / Python | Ubuntu, Python 3.12.3 |
| Docker | Docker Compose v5.4.0; docker running, no containers |
| edge_tts | 7.3.0 already installed on prod (Eva's fix's third surface is done) |
| Services | nginx + pulse-site.service running |
| Listening | 8442–8448 (V3 A2A), 9903–9906, 9222, 8765 |
Ruling carried — sizing corrected: Young confirmed 4 vCPU / 24 GB / 200 GB. My architecture note guessed 24 GB from a study and flagged it as unmeasured. The measurement is now the authority. Per-tenant ≈ 3 GB of the 24 GB → 8 tenants on the node before any scaling decision, not 2–3.
Prep tasks:
- ARM64 image policy — the compose must build/pull arm64. This is the single most likely silent failure (an amd64 image on ARM fails late, looking like a config bug).
- Reverse proxy — nginx is live with pulse-site; add per-tenant vhosts (
t<id>.ourdomain) and TLS. Routing per tenant is the front door. - Resource ceilings in every compose:
cpus+mem_limitper tenant so one tenant cannot eat the node (F9). - Storage layout —
/srv/tenants/<id>/{compose,data,bind-mounts}, Postgres volumes pinned. Backups to the 179 GB free. - Firewall — only 80/443 (and 22 to our admin path) exposed; the current 8442–8448/9903–9906 listeners are LAB-style loopback and must not be reachable from the public interface on a shared commerce node.
- Secrets layout — per-tenant
.envmode 0600, never in images or the repo (the vault's own discipline). /home/ubuntu/pulse-sitestays untouched. Prod hosts Pulse today; the commerce stack lands alongside, not over.
One measured risk stated plainly: the same node hosts Pulse (prod) and would host the commerce tenants. That is one noisy neighbour away from a Pulse outage — same class of defect as V2's fleet. Pulse gets a resource ceiling and an alert before tenant #1 is provisioned.
The 3-in-1 Commerce Package — architecture
Instruction (Young): "Package 3 products into single package as sell services."My lane: design and architecture. Mira: business plan. Min + Tam: package deep dive.
Authelia on prod — optimization + deployment design (2026-10-18)
Young: "how to optimise and use — Authelia covers the gate for all tenants, not just sites (Docs, Pulse)."

