DailyMark Restaurant QR Menu Platform — Design Specification
Date: 2026-08-09 Status: Draft for user review; working assumptions remain reversible until implementation starts Target repository: check-list / DailyMark Primary implementation stack: TypeScript, NestJS, PostgreSQL with FORCE RLS, Redis/BullMQ, Next.js management UI, Caddy, S3-compatible object storage
1. Summary
DailyMark will gain a multi-tenant restaurant-menu module. A restaurant manager can create or import a menu, review AI-assisted descriptions, translations, images, videos, and 3D assets, publish an immutable static version, and distribute a stable URL through a QR code. Each published menu is available on a tenant subdomain such as restaurant.dailymark.me and may also be bound to a verified custom domain.
The management plane remains part of the existing authenticated DailyMark application. The public delivery plane is isolated from it: published HTML and media are immutable artifacts in object storage, and a lightweight menu gateway serves the active artifact by request host. A failure in the management API must not invalidate an already published menu.
The platform collects privacy-oriented, tenant-scoped engagement analytics. OCR and all generative AI features are asynchronous and human-reviewed. AI never silently changes prices, ingredients, allergens, or publication state.
2. Goals
- Manage restaurant menus per tenant and assign them to one or more existing DailyMark locations.
- Publish deterministic, immutable static HTML bundles with atomic activation and rollback.
- Provide stable QR URLs on
*.dailymark.meand verified custom domains. - Import menu structure from PDF files and photographs/scans.
- Generate reviewable item descriptions, translations, and images with AI.
- Generate reviewable short video or optimized 3D assets for individual items.
- Collect and report analytics isolated by tenant, menu, publication, location, and item.
- Preserve accessibility, fast mobile loading, privacy, auditability, and predictable AI cost.
3. Non-goals for the first implementation cycle
- Ordering, payment, table service, delivery integration, stock synchronization, or POS integration.
- AI-generated ingredients, allergens, nutrition facts, or legal claims.
- Automatic publication of OCR or AI output.
- Real-time per-request server rendering of public menu content.
- Local 3D/video rendering on the DailyMark production VPS.
- Arbitrary custom JavaScript, HTML, or CSS supplied by tenants.
- Persistent cross-site advertising profiles or fingerprinting.
These exclusions keep the first release focused on menu publishing and content operations. The data model must not prevent later ordering or POS work, but no interfaces for those systems are part of this design.
4. Product assumptions
The following decisions are explicit defaults because no conflicting requirement was supplied:
- The feature is a module of the existing DailyMark product and reuses tenants, users, memberships, locations, roles, billing, and audit conventions.
- The initial customer is one restaurant or a small chain. A menu may be assigned to several locations. Location-specific item overrides are deferred; separate menus cover differences in the first release.
- Changes are made in a draft and become public only through an explicit Publish action.
- Every publication is immutable. Activation and rollback change only a small active-version pointer.
- Import MVP accepts PDF and common image formats. DOCX, XLSX, CSV templates, and POS imports are later adapters.
- OCR and AI results are suggestions requiring explicit review.
- A menu has a primary language. RU, EN, and SR translations may be generated by AI and require review.
- Heavy media generation is delegated to external providers through provider-neutral adapters.
- Public analytics is cookieless by default.
5. Success criteria
The feature is successful when all of the following are demonstrably true:
- A tenant manager can create, preview, publish, and roll back a menu without platform-admin intervention.
- A stable QR code continues to open the newest active publication after edits and rollbacks.
- A published menu remains available while the authenticated management application or primary API is unavailable.
- Cross-tenant reads and writes fail in API and database tests.
- A PDF or scan can be converted into a reviewable structured proposal without mutating the draft before acceptance.
- An item can receive reviewed AI descriptions, translations, images, video, or 3D media with provenance and cost recorded.
- A tenant can see only its own analytics, broken down by menu, location, item, language, QR campaign, and publication.
- A failed build never replaces the current active publication.
- Public pages satisfy the bundle budgets and accessibility rules in section 19.
6. System boundaries
6.1 Management plane
The management plane runs in the existing authenticated product:
apps/web: menu editor, import review, AI/media studio, preview, publication history, domains, QR campaigns, and analytics dashboard.apps/api: menu domain model, RBAC, FORCE RLS persistence, jobs, provider orchestration, publication coordination, audit log, and analytics ingestion/query APIs.- Redis/BullMQ: OCR, AI, media, publish, aggregation, retention, and cleanup queues.
- PostgreSQL: mutable draft data, immutable publication metadata, domain verification state, job/cost metadata, and analytics records/aggregates.
6.2 Public delivery plane
apps/menu: a small read-only TypeScript service that resolves the normalized request host to an active publication manifest and serves immutable files from object storage.- Caddy: TLS termination and routing for the wildcard DailyMark domain and verified custom domains.
- S3-compatible object storage: source uploads, private generated candidates, approved media, public immutable publication bundles, and durable domain manifests.
- A CDN is not required for the first launch. The immutable URL/cache contract permits adding one later without changing publication semantics.
The menu gateway does not use a user session, accept tenant IDs from the browser, or render mutable database content. The active host manifest is written only by the publish/domain workflows.
6.3 Analytics plane
The first implementation uses a dedicated analytics module and hostname, analytics.dailymark.me, inside the existing API deployment. It accepts only a strict public event schema. Analytics failure is fail-open for menu viewing: the browser discards an event if delivery fails.
7. High-level architecture
The publication path is:
- A manager edits tenant-scoped draft entities in PostgreSQL.
- Publish validation creates an immutable database snapshot and enqueues a build.
- The publish worker renders localized HTML, CSS, minimal JavaScript, manifests, and optimized media references.
- The worker uploads the complete bundle under a publication-version prefix; individual files carry content hashes and checksums.
- The worker reads back the manifest, verifies checksums and required files, and performs a smoke render.
- Only after verification does it atomically replace the active host manifest.
- The menu gateway sees the new manifest and serves the new version. Existing immutable assets stay cacheable.
The active host manifest is small and uses Cache-Control: no-cache with ETag revalidation. All versioned files use long-lived immutable caching.
8. Domain model
Every mutable business entity carries tenant_id and is protected by FORCE RLS unless explicitly identified as a global system record.
8.1 Core entities
menus
id,tenant_id- internal name and public slug
- primary locale and currency
- status:
draft,publish_pending,published,archived active_publication_id- created/updated timestamps and actors
menu_locations
Join table from a menu to existing locations. A location may have several menus for distinct contexts, but domain/QR configuration identifies the intended public menu.
menu_sections
- localized section identity and sort order
- enabled state
menu_items
- section, stable item identity, sort order, enabled/available state
- authoritative price and currency
- weight/volume and unit
- authoritative ingredients and allergen declarations entered or confirmed by a human
- dietary labels
- publication visibility flags
menu_item_variants
Size or option, display label, price delta or absolute price, sort order, and availability. Add-ons that require combinatorial ordering behavior remain out of scope.
menu_translations
- owner type/id, locale, field, value
- status:
missing,human,ai_draft,reviewed - provider/model/prompt reference for AI-generated values
- reviewer and review timestamp
media_assets
- owner item, media type:
image,video,model_3d,poster - private/public object keys and content hashes
- MIME, dimensions, duration, byte size
- lifecycle:
candidate,needs_review,approved,rejected,published,deleted - provenance: uploaded/generated, provider, model, prompt, source asset
- rights confirmation and safety result
- fallback asset relationship
menu_domains
- globally unique normalized hostname
- tenant/menu relationship
- kind:
dailymark_subdomainorcustom - verification method/token and observed DNS state
- status:
pending,verified,active,error,disabled - TLS status and last check timestamps
Hostnames are stored in lower-case ASCII form after IDNA normalization. Reserved DailyMark hostnames, public suffixes, IP literals, localhost names, and invalid labels are rejected.
menu_publications
- immutable ID and monotonically increasing menu version
- tenant/menu and source draft revision
- exact JSON snapshot and schema version
- bundle prefix, manifest, checksums, and renderer version
- state:
building,verified,active,superseded,failed - publisher and timestamps
- signed public analytics token
menu_import_jobs
Source object, detected type, page count, OCR provider, progress, structured proposal, per-field confidence, warnings, cost, state, and error metadata.
generation_jobs
Tenant/item, operation, provider/model, normalized input hash, request metadata, estimated/actual cost, progress, provider job ID, candidate assets/text, retries, timestamps, and terminal result.
qr_campaigns
Stable menu/domain URL plus campaign code, label, location/table note, generated SVG/PNG object keys, active state, and analytics grouping.
menu_audit_events
Actor, tenant, action, target, before/after summary, correlation ID, UTC timestamp, and origin. Prompts may be retained, but provider credentials and full private uploads never enter audit records.
8.2 Analytics entities
menu_analytics_events: append-only raw event records partitioned by time.menu_analytics_daily: tenant/menu/publication/location/item/campaign daily aggregates.menu_analytics_retention_runs: auditable deletion/aggregation progress.
The public collector derives tenant and menu identity only from a server-signed publication analytics token. Client-supplied tenant identifiers are ignored.
9. Roles and permissions
- Owner: all menu actions, domain management, AI/media budgets, analytics export/deletion, and publication rollback.
- Manager: create/edit/import/review/publish/rollback menus, manage campaigns, and view analytics. Domain removal and budget changes remain owner-only.
- Editor: edit drafts, run allowed imports/generations within tenant limits, and submit review candidates. Cannot publish, roll back, or manage domains.
- Platform admin: operational visibility and support tools through the existing admin application; business content access must be audited and follow impersonation rules.
Publication and rollback require manager or owner. Any change to price, ingredients, or allergens after a review invalidates publication readiness until validation runs again.
10. Menu management experience
The management UI contains these areas:
- Menus list: location assignments, domain, current version, last publication, health, and outstanding review count.
- Editor: sections/items, variants, pricing, availability, translations, allergens/dietary fields, and media.
- Import: upload, OCR progress, proposal/diff review, field confidence, merge policy, and acceptance summary.
- AI Studio: description/translation/image/video/3D actions, variants, cost estimate, candidate comparison, approval/rejection, and history.
- Preview: draft snapshot in mobile and desktop frames, locale switching, slow-network/media fallback, and publication validation.
- Publications: build progress, checksum/health, current/previous versions, diff, and rollback.
- Domains & QR: subdomain reservation, custom-domain instructions, verification/TLS health, QR campaign creation, and downloadable SVG/PNG.
- Analytics: metrics and comparisons defined in section 16.
The existing DailyMark design system remains authoritative for management UI. The public menu uses an isolated, accessible theme generated from a constrained restaurant branding configuration; it does not accept arbitrary tenant CSS.
11. Import and OCR workflow
- The browser requests a scoped upload and sends a PDF or image directly to private object storage.
- The API verifies declared size/type and enqueues validation.
- The worker performs MIME sniffing, malware/quarantine checks, decompression limits, image normalization, and page extraction.
- OCR returns text and geometry. A structuring model converts it into sections, items, prices, currencies, weights, and description candidates.
- Deterministic validation checks price formats, currencies, duplicate items, missing names, suspicious confidence, and inconsistent page structure.
- The UI shows the source page beside a structured proposal. Every field has confidence and provenance.
- The user chooses create, update, skip, or resolve for proposed records. Prices, ingredients, and allergens require explicit confirmation.
- Acceptance writes one auditable draft transaction. Rejection leaves the draft unchanged.
Import jobs are resumable. A partial provider result never mutates the menu. The source file, normalized pages, OCR output, structured proposal, and acceptance decision have separate retention policies so the raw document can be removed without breaking the accepted menu.
12. AI text, translation, and image generation
All providers implement internal contracts. The existing LiteLLM path is reused where the selected text/vision model supports the required structured output. Image providers use a separate adapter because their job and asset semantics differ.
12.1 Text rules
- Input consists only of manager-approved item facts.
- The model may write concise marketing copy and translations.
- The model must not invent ingredients, allergens, origin, awards, health claims, nutrition values, or preparation methods not present in approved facts.
- The response is schema-validated and stored as one or more candidates.
- Accepting a candidate records actor, prompt template version, model, and source facts.
12.2 Image rules
- The user selects style/aspect ratio and confirms that the requested depiction is appropriate.
- Two to four candidate images are generated per request unless a tariff sets a lower limit.
- Every candidate retains provider/model/prompt/provenance and safety result.
- Approval creates optimized responsive formats and a poster fallback; rejection retains only minimal job metadata after the candidate-retention window.
12.3 Cost controls
- Tenant monthly budgets by operation type.
- Per-job estimate shown before enqueueing expensive work.
- Global and per-tenant concurrency limits.
- Idempotency on tenant, operation, normalized input, and explicit regenerate nonce.
- Actual provider usage/cost stored after completion.
- Owner-visible usage dashboard and threshold alerts.
13. Video and 3D generation
Video and 3D use asynchronous provider adapters with either signed webhooks or bounded polling.
13.1 Video output contract
- Short restaurant-item clip.
- MP4 and WebM delivery variants when conversion supports both.
- Muted by default, no autoplay with sound.
- Poster image and ordinary image fallback.
- Duration, dimensions, codec, and byte-size limits enforced before approval.
13.2 3D output contract
- Optimized GLB/glTF delivery asset.
- Validated scene bounds, materials, texture references, polygon budget, and byte-size budget.
- Poster image and ordinary image fallback.
- No scriptable content or external texture URLs.
13.3 Public behavior
- Initial HTML never downloads video or 3D assets.
- The visitor explicitly opens rich media.
- Reduced-motion, unsupported browser, data-saver, timeout, or asset error falls back to a poster/image.
- Rich-media failure never hides item facts or price.
14. Static publication
14.1 Validation gate
Publish is blocked when any enabled item lacks a reviewed name or valid price, any configured locale violates the tenant's completeness policy, an approved rich-media asset lacks fallback, a domain is inactive, or a previous build for the same draft revision is already running.
Warnings that do not block publication include missing optional descriptions, missing optional images, and unreviewed non-primary translations that the tenant has chosen not to publish.
14.2 Snapshot and render
- The API reads the complete draft in one tenant-scoped transaction.
- It creates an immutable snapshot with explicit schema and renderer versions.
- The renderer consumes only the snapshot; it never queries mutable menu tables.
- Rendering is deterministic for the same snapshot and renderer version.
- Generated content is escaped/sanitized and no tenant-authored executable code is included.
14.3 Bundle contract
Each bundle includes:
- localized entry HTML files;
- shared critical CSS and minimal JavaScript;
- a web app manifest only if offline behavior is implemented in that workstream;
- item/media manifests;
- accessibility metadata;
- analytics configuration with signed publication token;
- checksums and renderer/schema versions;
- a machine-readable health manifest.
14.4 Activation and rollback
The worker uploads to a unique version prefix and verifies it before activation. Activation atomically updates the host manifest to the verified publication. Rollback points the host manifest to an earlier verified bundle; it does not rebuild it. Active and configured rollback publications are protected from retention cleanup.
15. Domains and QR codes
15.1 DailyMark subdomains
- A tenant reserves an available normalized slug.
- Wildcard DNS sends
*.dailymark.meto the public menu ingress. - A wildcard certificate is obtained through a DNS challenge because wildcard issuance cannot use the normal HTTP challenge.
- Reserved names include current product hosts and operational prefixes.
15.2 Custom domains
- Owner enters a hostname.
- DailyMark supplies CNAME/A and TXT verification instructions.
- A verification job confirms control and correct routing.
- The domain becomes
verified, thenactiveonly after the public manifest exists. - Caddy On-Demand TLS may issue a certificate only when its internal
askendpoint confirms an exact activemenu_domainsmatch.
The ask lookup uses an exact normalized hostname context rather than an unrestricted cross-tenant query. Invalid, disabled, unverified, reserved, or unknown domains are denied. Requests are rate-limited and auditable.
15.3 QR behavior
- QR codes contain stable public URLs without publication version IDs.
- Campaign codes are short opaque identifiers, not tenant IDs.
- SVG is the canonical printable artifact; PNG variants support common print workflows.
- The management UI includes a scan test and minimum-size/contrast guidance.
- Publication, rollback, or CDN changes do not require QR reprinting.
16. Analytics
16.1 Event schema
MVP events:
menu_viewitem_opensearchfilter_applylocale_changemedia_startmedia_completecta_clickqr_campaign_openmenu_load_errormedia_error
Each event has an event ID, signed publication token, session ID, event type, item/campaign reference when applicable, locale, coarse device information, client timestamp, and schema version. The server adds authoritative tenant/menu/publication/location identities and received UTC time.
16.2 Privacy defaults
- No advertising cookies, cross-site identifiers, or fingerprinting.
- A short-lived random session ID is used for approximate unique sessions.
- IP is used transiently for abuse controls and coarse geography, then discarded.
- Referrer and URL are stripped of query values not explicitly allow-listed.
- Persistent visitor identity remains disabled until a separate consent and legal review authorizes it.
- Raw events are retained for 90 days.
- Daily aggregates are retained for 24 months.
- Tenant export and deletion operations are auditable and tenant-scoped.
16.3 Ingestion
- Browser batches use
sendBeaconwhen possible. - Collection never blocks navigation or menu rendering.
- Strict schema validation rejects unknown types and oversized payloads.
- Event ID deduplication, token verification, rate limiting, and bot filtering run before persistence.
- The browser's tenant ID is never trusted.
16.4 Dashboard
The tenant dashboard includes views and approximate unique sessions, QR campaign sources, item/category popularity, searches without results, languages, devices, media engagement, CTA conversion proxies, publication comparisons, load performance, and media errors.
Platform-admin reporting may show cross-tenant operational aggregates but must not expose restaurant content or tenant-level visitor detail without an audited support action.
17. Security and privacy controls
- FORCE RLS and explicit tenant context on all tenant business tables.
- Exact-host scoped policy or equivalent restricted database path for custom-domain authorization.
- Signed public analytics token; no public endpoint accepts authoritative tenant identity from the client.
- Presigned uploads limited by object prefix, content length, expiry, and operation.
- MIME sniffing, malware quarantine, decompression/page/pixel limits, and no executable document processing.
- Private buckets/prefixes for source documents and candidates; only publication assets are public through the gateway.
- Strict CSP, safe templating, URL allow-lists, and sanitized text.
- Signed provider webhook validation, replay protection, timestamp tolerance, and idempotency.
- Secrets available only from runtime environment/config services and never written to jobs, prompts, logs, or bundles.
- Audit logs for publication, rollback, domain, budget, review, export, deletion, and platform support actions.
- Data retention workers operate by explicit entity lifecycle and never delete active/rollback artifacts.
18. Error handling and recovery
18.1 Jobs
Job states are queued, running, provider_processing, needs_review, approved, rejected, failed, cancelled, or expired. Temporary errors use bounded exponential backoff. Permanent validation/provider errors become actionable terminal failures. Exhausted jobs enter a dead-letter queue with correlation IDs.
18.2 Import and generation
No job mutates a menu before explicit acceptance. Provider timeouts, malformed structured output, rejected safety results, and conversion failures preserve the original draft and display a specific remediation.
18.3 Publication
A failed build or verification leaves the active pointer unchanged. Uploaded incomplete prefixes are private and later cleaned. If activation confirmation is ambiguous, the worker reads the active manifest and resolves state idempotently before retrying.
18.4 Delivery
The gateway caches host manifests and immutable assets. A temporary database/API outage does not affect delivery. Missing rich media falls back to poster/image. An unknown or inactive host returns a branded 404 without revealing tenant data.
18.5 Analytics
Analytics is lossy by design. A failed beacon is discarded rather than delaying or retrying indefinitely. Aggregation and retention jobs are idempotent and resumable.
19. Testing and quality gates
19.1 Unit tests
- price/currency/unit validation;
- slug and international hostname normalization;
- publication readiness;
- deterministic renderer and manifest generation;
- prompt input policy and structured response validation;
- event schema and signed analytics token;
- job transition and cost-limit logic.
19.2 Database/API integration tests
- migrations create every entity, index, constraint, and FORCE RLS policy;
- tenant A cannot read or mutate tenant B menus, publications, jobs, assets, domains, or analytics;
- exact-host TLS authorization sees only the matching active domain;
- global hostname uniqueness is enforced without weakening tenant isolation;
- publication activation/rollback remains atomic under retries;
- duplicate provider webhooks and analytics events are idempotent.
19.3 Contract and fixture tests
- provider adapters run against deterministic fakes and signed webhook fixtures;
- PDF/photo golden fixtures produce expected structured proposals and confidence warnings;
- renderer golden snapshots are versioned deliberately;
- media conversion validates allowed codecs, GLB structure, external references, fallbacks, and budgets.
19.4 End-to-end tests
- manager creates, imports, reviews, previews, publishes, and rolls back a menu;
- editor cannot publish;
- owner verifies a custom domain through a local DNS/TLS test harness;
- QR URL stays stable across versions;
- public locale switching, search, filters, media fallback, analytics, and error states work on mobile;
- keyboard, screen-reader labels, contrast, focus, reduced motion, and zoom are checked.
19.5 Performance and resilience
- critical JavaScript at most 50 KB gzip;
- HTML + CSS + critical JavaScript at most 150 KB gzip, excluding media;
- images are responsive and lazy-loaded outside the initial viewport;
- video and 3D never enter initial load;
- menu gateway and analytics collector pass the agreed tenant/load model before launch;
- management API outage does not break an existing published menu;
- backup/restore and publication rollback drills pass before production launch.
20. Observability
Metrics and structured logs cover queue depth/age, job duration/failure/cost, OCR confidence distribution, publication duration/failures, gateway cache/latency/status, custom-domain verification/TLS failures, analytics accept/reject/drop rate, storage growth, and tenant budget consumption.
Logs use correlation IDs and stable entity IDs. They exclude source-document contents, full prompts containing private text, generated binary data, credentials, IP addresses after transient abuse processing, and unfiltered provider responses.
Alerts focus on user-visible failures: active menu availability, publication failure rate, custom-domain TLS failures, runaway queue age/cost, and storage thresholds.
21. Delivery workstreams and dependency order
This product is too large for one safe implementation batch. Work proceeds through six bounded workstreams:
Workstream 1 — Core menu management and static publication
Domain entities, RLS/migrations, management editor, preview, snapshot/renderer, object storage, immutable bundle, menu gateway, publication history, rollback, and base observability.
Workstream 2 — Domains and QR
Wildcard DNS/TLS, subdomain reservation, custom-domain verification, restricted On-Demand TLS authorization, QR campaigns, and domain health operations.
Workstream 3 — Document import
Secure upload, PDF/image normalization, OCR/structuring adapters, confidence model, diff review, merge transaction, fixture suite, and retention.
Workstream 4 — AI text and images
Provider contracts, prompt policies, descriptions/translations/images, review UI, provenance, safety, cost accounting, budgets, and billing limits.
Workstream 5 — Video and 3D
Asynchronous job adapters, webhooks/polling, media conversion/validation, player/viewer, fallbacks, accessibility, cost controls, and public performance gates.
Workstream 6 — Tenant analytics
Signed event schema, collector, partitioned raw data, aggregation/retention, dashboard, export/deletion, privacy review, and load tests.
Workstreams 1 and 2 establish the publish/delivery contract. Workstreams 3–6 consume that contract and may be developed independently after their shared entity/provider interfaces are frozen. Video/3D follows image generation because it reuses media lifecycle, moderation, storage, and budget controls.
22. Launch gates
Production launch requires:
- completed privacy/legal review for analytics, generated content, retention, custom domains, and restaurant responsibility for factual menu data;
- provider commercial terms, regional availability, data-processing terms, and cost ceilings recorded;
- production object-storage lifecycle, backup, restore, and cost alerts configured;
- wildcard DNS/TLS and custom-domain staging drills passing;
- published-menu availability monitor and rollback runbook active;
- cross-tenant/RLS, upload security, webhook, bundle, accessibility, and performance suites green;
- operator documentation for failed jobs, stuck publications, domain verification, TLS denial, storage cleanup, and analytics deletion;
- tenant-facing copy stating that AI output must be reviewed and that factual menu/allergen responsibility remains with the restaurant.
23. Design references
- Caddy Automatic HTTPS and On-Demand TLS: https://caddyserver.com/docs/automatic-https#on-demand-tls
- Caddy On-Demand TLS global authorization: https://caddyserver.com/docs/caddyfile/options#on-demand-tls
- Repository visual contract: root
DESIGN.md - Existing production ingress/runtime: root
Caddyfileanddocker-compose.prod.yml - Existing tenant context and RLS conventions:
apps/api/src/tenant-context.tsand current RLS migrations
24. Follow-up planning contract
The implementation plan must turn each workstream into ordered, test-driven tasks with file-level ownership, dependencies, migrations, rollout gates, management checklists, and acceptance evidence. It must not treat this umbrella design as one implementation batch. The first executable plan starts with Workstream 1 and includes shared foundations needed by Workstream 2; later workstream plans may proceed only after their prerequisite contracts are complete.