Files
marketplaces/docs/backend/BACKEND-HANDOFF.md
sdarbinyan f9e09b1757 docs(backend): harvest platform mechanisms into the contracts (Wave 2, FH-E.1-E.4)
Writes the 14 harvested mechanisms from FORK-ANALYSIS-2026-08-21.md into
the backend contracts. Each section is dated 2026-08-21 and tagged FH-*
so any wording traces back to why it is worded that way.

The through-line: several contracts stated correctness as behaviour
("the webhook must be idempotent"). Behaviour written as an if-statement
gets deleted by a refactor and the failure mode is a double charge. These
sections restate it as schema and mechanism.

PHASE-3  3.1 conditional-write reservation, 409 on zero rows, cart-wide
             rollback, 15 min TTL
         3.2 InventoryMovement append-only journal with resultingAvailable
         6   bulk import idempotent by SKU, rollback while unsold
         6a  digital code pools, revealed only when paid
PHASE-7  5   unique constraints for payment idempotency and webhook
             replay, insert-first handling, signature over raw body,
             24h poll as reconciliation not primary
TRACK-S  2.1 session model - 32 bytes stored as SHA-256 only, HttpOnly,
             one cookie per contour, Argon2id params, mandatory TOTP
         2.2 origin allowlist ahead of routing on every cookie mutation
         4.2 AES-256-GCM envelope for stored secrets, HMAC fingerprints
         8a  order manager as a separate contour, scoped by membership
             rows rather than by configuration
PHASE-9  5.1 revision immutability, version = max+1, pointer flipped
             in-transaction, operational state does not travel
         5.2 clone carry / no-carry list, inventory to zero
         5.3 signed read-only preview, non-GET 404s while previewing
         6   host normalization, verifiedAt required, cache invalidation
PHASE-10 3a  server re-runs the editor's validation, clamp-and-fallback
PHASE-2  3.1 order publicToken, snapshot completeness, never updated

FH-2.12 rejected on the merits: our marketplace lifecycle state machine
is richer than theirs, adopting it would be a downgrade. Recorded in the
TODO so it is not raised again.

Also adds BACKEND-HANDOFF.md sections 0 and 0a - nine falsifiable
invariants as a release gate, each cross-referenced to the contract that
specifies it, plus PR and release discipline. And ADR-0006 recording what
we take, what we reject, what we keep because ours is better, and the
organizational question it deliberately does not settle.

No implementation changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 11:12:05 +04:00

13 KiB
Raw Blame History

Backend handoff — start here

Single entry point for a backend developer picking this up cold. Written 2026-08-18.

0. Invariants — the acceptance gate

Added 2026-08-21 (FH-E.1). Nine statements. Each is falsifiable, each has a test, and a release that violates any of them does not ship regardless of what else is finished. They come before the contracts because a contract can be read selectively and these cannot.

  1. The public tenant is determined by verified Host alone. No public endpoint accepts a marketplaceId from the browser.
  2. The price of an order is computed by the backend. A price arriving in a request is ignored, never validated-and-used.
  3. Stock and reservation change atomically. Two buyers racing for the last unit produce one payable order.
  4. Payment creation and webhook receipt are idempotent, enforced by unique constraints rather than by handler logic.
  5. Provider credentials never leave the backend — not in a response, not in a bundle, not in a log.
  6. A published revision is immutable. Rollback creates a new revision; history is never rewritten.
  7. Rolling back design does not roll back live inventory, orders, or payments.
  8. No user reads a marketplace they are not assigned to — through the UI or through a direct API call.
  9. Every administrative mutation leaves an audit record naming actor, action, before and after.

Where each is specified: 1 and 67 in Phase 9 §56; 2 in Phase 1 §5; 3 in Phase 3 §3.1; 4 in Phase 7 §5; 5, 8 and 9 in Track S §2, §3, §4.2.

0a. How work lands

Added 2026-08-21 (FH-E.2, FH-E.3).

One functional area per pull request. A PR carries: what it does, screenshots where there is UI, API changes, migrations, test evidence, security impact, and a rollback plan. Never change a payment, inventory, or order state machine in the same PR as a redesign — those two things fail differently and must be revertable separately.

Migrations are expand/contract. A migration and the frontend change that depends on it belong to one release train, but they may be separate PRs, and the expand step must be deployable on its own.

A release is not "the build passed." Each one records: version, migrations applied, healthcheck result, post-deploy smoke result, production dependency audit, and the rollback path actually available. A local build succeeding and a UI existing are not evidence of production readiness — the deploy and the smoke check are.

1. What this is

marketplaces is a multi-tenant marketplace platform frontend (Angular 22). The frontend is built and waiting; there is no backend yet. Every wire contract the backend needs to implement is already written and sitting in this directory — see README.md for the full index and build order.

Contract sections dated 2026-08-21 and tagged FH-* come from FORK-ANALYSIS-2026-08-21.md — a review of a parallel platform implementation that has a working backend. They are mechanisms that implementation already proved, harvested deliberately; the tag is there so you can trace any one of them back to why it is worded the way it is.

1a. Multi-tenancy — the thing that shapes every endpoint

The final executable infrastructure/backend contract is TENANT-API-DOMAIN-HANDOFF.md. Follow it for hostname normalization, CORS, nginx, TLS, CI secrets, and acceptance checks.

One deployed bundle serves every customer domain. There is no per-tenant build. The chain is:

  1. TenantResolverService reads the complete current browser hostname and protocol (localhost still uses the development proxy).
  2. ApiConfigService uses one API host per base domain: both example.com and store1.example.com use api.example.com.
  3. ApiBootstrapProvider, auth, legacy API calls, and versioned /api/... calls all use that same base.
  4. Each base domain owns one API DNS/TLS/reverse-proxy entry. nginx validates the browser origin and forwards the complete storefront hostname as X-Storefront-Host.
  5. Backend tenant lookup uses that trusted storefront hostname, not the shared API Host; frontend nginx remains default_server / server_name _, so any attached storefront domain receives the same bundle.

What this means for you: the bootstrap endpoint is the single most important thing to build after auth. Every request must be tenant-scoped server-side, and a tenant must never be able to read another tenant's data — return 403, not an empty result (see TRACK-S §2). The frontend supplies the tenant identity from the hostname; the backend must treat that as an untrusted hint and derive real scope from the authenticated session.

Constraints already fixed by the frontend design (see the platform-vision facts in docs/context/): no marketplace-specific code or hardcoded marketplace data in the frontend; bootstrap carries only what is needed before app start (branding, languages, homepage layout, navigation, enabled widgets, footer pages) and never products, orders, cart, or users.

PHASE-9 covers the marketplace registry, domain attachment, and publish/revision model.

2. Read in this order

  1. README.md — index of all contracts, build order, and what's deliberately excluded.
  2. PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md — start here. Everything after depends on the money model.
  3. Phases 2→4 — the rest of the launch gate (orders, catalog, connectors).
  4. TRACK-S-SECURITY-RBAC-CONTRACT.mdgates the launch. Today the admin role model is decorative: nothing server-side enforces any permission. §8 covers per-marketplace bootstrap admin accounts and self-service sub-admin management.
  5. TRACK-A-ANALYTICS-CONTRACT.md — longest lead time, start it in parallel with Phase 1.
  6. Phases 5→10 — post-launch-gate.
  7. PARTNER-PROVISIONING-API-CONTRACT.md — the inbound partner API. Read it before implementing Phase 1, not after: it adds RoutingContext to CheckoutSession/PaymentIntent/Payment (Phase 1 §6.5) and two levels above Marketplace (Phase 9 §1). Building the partner API itself can wait; carrying its routing dimension in the payments tables cannot.

../../BACKEND-API-REFERENCE.md documents the current live API surface (legacy endpoints, error envelope, mock-only areas). New endpoints use /api/v2/... namespaces; legacy endpoints are not being migrated.

3. Auth — read before writing any endpoint

Auth is no longer part of this repo. It lives in @marketplaces/auth, published from vitanovaPackages. See ../PACKAGES-USAGE.md for the full client surface. What matters on the backend side:

Two mechanisms exist client-side.

  • Telegram QR/session (live). Endpoints under {authApiUrl}/users/sessionsPOST to create, GET /{id} to poll, DELETE /{id} to log out. Both customer and admin login call the same endpoints; only client-side storage differs. The response shape is normalized permissively client-side (many key spellings accepted), but a clean implementation should return { webSessionID, user: { userId, username, firstName, lastName }, status, expiresAt }.
  • Ed25519 challenge/response (not built). GET /api/admin/auth/challenge, POST /api/admin/auth/verify, POST /api/admin/auth/refresh, POST /api/admin/auth/logout. Contracts in TRACK-S and the package's ed25519/models/auth-api.model.ts. Until these ship, the client shows a backend-unavailable screen — nothing is mocked.

The critical gap: the session API has no concept of "admin." The frontend cannot distinguish an admin session from a customer one — it only chooses where to store the result. Every admin endpoint must independently verify authorization server-side. Client-side guards are UI convenience, never security. This is the single most serious open issue in the system.

Admin requests carry AdminWebSessionID: <sessionId> (and Authorization: Bearer <token> once admin JWTs exist) on paths containing /admin/, /backoffice/, /builder/, /media/.

4. Environment / infrastructure state

Dev server 213.21.246.138 (user seto, sudo, SSH key provided separately).

Thing State
nginx 1.24 Installed, running. Config at /etc/nginx/sites-enabled/marketplaces-dev.conf. Serves frontend from /srv/marketplaces/current/frontend, backoffice from /srv/marketplaces/current/backoffice, proxies /api/127.0.0.1:8080. /health returns ok.
Go toolchain Installed (/usr/local/bin/go).
Backend service on :8080 Not running. Nothing is listening. /srv/marketplaces/current/api is an empty shell. nginx's /api/ proxy currently 502s.
PostgreSQL Installed but inactive. Needs starting, a database, a user, and schema before anything works.
Shared packages @marketplaces/auth installs over plain git from a release branch — no registry, token, or tunnel needed. npm install works out of the box.
Verdaccio (npm registry) Running in Docker on port 4873, but superseded and unused — nothing depends on it. See ../PACKAGE-EXTRACTION.md §5.
Firewall (ufw) Active. 80/tcp, 443/tcp, OpenSSH.
TLS / certbot Not installed. No certificates. Everything is plain HTTP today. For multi-tenant this is real work: every customer domain needs a certificate (per-domain issuance, or a wildcard if all tenants sit under one apex).
DNS / dynamic subdomains Not set up. No domain currently points at the server (reverse DNS is the provider default silky-bronze.ptr.network). No wildcard record, no per-tenant subdomain automation, no Hostinger DNS integration. The application is fully multi-tenant (§1a) — this is the missing infrastructure underneath it. PHASE-9 specifies the target.
Frontend deploy (CD) None. Pushing to main deploys nothing. architecture-governance.yml builds and checks boundaries but has no deploy step, and nothing writes to /srv/marketplaces/current/frontend. Deploys are manual today.
CI runner None on this server; sources.vitanova.network CI runs elsewhere.

5. To get a working dev environment

Nothing here is done yet — this is the setup a backend dev does on day one.

  1. Start and configure PostgreSQL; create the database and application user.
  2. Design the schema from the Phase 14 contracts (schema design is explicitly the backend's own call — the contracts specify entities, endpoints, and invariants, never tables). Tenant scoping belongs in the schema from day one; retrofitting it is painful.
  3. Build the API service, listen on 127.0.0.1:8080. nginx already proxies /api/ to it.
  4. Implement the bootstrap config endpoint (§1a) — without it the frontend cannot render for any tenant.
  5. Implement the Telegram session endpoints — the login flow is fully built client-side and blocked only on these.
  6. Implement GET /api/identity/v1/session/permissions (TRACK-S §2) — frontend route guards derive from it.
  7. Seed per-marketplace bootstrap admins (TRACK-S §8): login = marketplace slug, password = {slug}2026$, mustChangePassword: true.

Steps 46 unblock the entire frontend. Everything after is feature work.

6. Frontend deploy

git clone <marketplaces repo>
npm install     # pulls @marketplaces/auth over git, no credentials needed
npm run build   # -> dist/dexarmarket

Angular 22, Node 24+. nginx serves /srv/marketplaces/current/frontend. deploy.yml builds and atomically deploys pushes to main; one deployment updates every domain at once. Before activation, the workflow reconciles TLS, exact CORS, and reverse proxying for every host in STOREFRONT_DOMAINS. Production deployment requires the documented CI secrets and the one-time server-setup.sh run.

7. Known open decisions

  • Registry reachability for CI (reverse proxy + TLS, or a different registry entirely).
  • Backend ownership. Answered 2026-08-18: implemented by a separate backend developer against this contract set.
  • Additional payment providers (wallets, BNPL) — Phase 7 §4.
  • Per-connector marketplace adapters — written per partner at onboarding, Phase 4 §8.
  • Backfill of Company/Project/PaymentPoint for existing marketplaces — sequence specified in Phase 9 §1.2, not yet scheduled.