restaurant/docs/architecture.md
Padreug 42a8b08a5b docs: Obsidian-style vault under docs/
Add a navigable Obsidian vault as the project's first-class
technical documentation. Notes cross-reference with [[wikilinks]];
docs/index.md is the Map of Content.

New notes:
  index.md             MOC, entry point
  architecture.md      what the extension owns vs what lives outside
  data-model.md        entity-by-entity schema reference
  menu-tree.md         the arbitrary-depth tree concept
  order-flow.md        state machine + invoice listener + print
  nostr-layer.md       kinds 0/30402/5/1059, signing, t-tags
  api-reference.md     endpoint catalog by audience
  cms.md               Vue 3 + Quasar 2 UMD conventions, q-tree
  webapp-integration.md  multi-restaurant cart pattern + atomicity
  glossary.md          domain terms

Existing notes (kept as-is):
  adr-0001-menu-tree.md  the storage choice rationale
  design-conversation.md trimmed transcript

README.md adds a Documentation section pointing at docs/index.md
with the headline note list. Each note links to ~3-5 others; the
vault forms a connected graph.

A project-level memory rule (saved outside the repo) commits us to
keeping these docs in sync as the code evolves: any commit that
materially changes schema, API, order flow, Nostr surface, CMS
conventions, or webapp integration must update the relevant note(s)
in the same commit.
2026-05-09 07:11:06 +02:00

89 lines
4.2 KiB
Markdown

# Architecture
The restaurant extension is the **operator's CMS** for one or many
restaurants on a single LNbits account. Customer-facing UIs (kiosks,
mobile apps, the AIO webapp) live outside the extension and consume it
over REST + Nostr.
## What this extension owns
- Restaurant profile rows and per-restaurant Nostr identity.
- The [[menu-tree]] (`menu_nodes` + `menu_items` + `modifier_groups` +
`modifiers` + `availability_windows`).
- The [[order-flow|order pipeline]] (`orders`, `order_items`,
`print_jobs`).
- Publishing the [[nostr-layer|menu to Nostr]] as NIP-99 listings.
- The [[cms|operator console]] under `/restaurant/...` (Jinja +
Quasar 2 UMD).
- A REST [[api-reference|API]] under `/restaurant/api/v1/...`.
## What lives outside the extension
| Concern | Where |
|---|---|
| Customer kiosk / mobile / web | `~/dev/webapp` ([[webapp-integration]]) |
| Multi-restaurant aggregation (festivals, food courts, collective spaces) | NIP-51 lists, curated externally |
| Lightning wallet, payment routing, user auth | LNbits core |
| Nostr relay connection | `nostrclient` extension |
| Thermal printer | `printer-pi` (subscribes to a webhook or Nostr event) |
## High-level topology
```
LNbits instance
┌────────────────────────────────┐
│ Restaurant ext │
│ ├── REST /restaurant/api/v1│
│ ├── CMS /restaurant/... │
│ ├── Nostr publisher │──────┐
│ └── Invoice listener │ │
│ (settle, decrement, │ │
│ queue print) │ │
└─────────┬──────────────────────┘ │
│ ▼
│ ┌─────────────────────────┐
│ │ nostrclient ext │──→ relays
│ └─────────────────────────┘
┌──────────────┐ ┌─────────────────────┐
│ printer-pi │ ◀──────│ webapp / AIO │
│ (subscribes) │ │ (customer, multi- │
└──────────────┘ │ restaurant cart) │
└─────────────────────┘
```
## Lifecycle
`__init__.py` registers three permanent tasks on extension start
(`create_permanent_unique_task`):
1. **Invoice listener**`tasks.wait_for_paid_invoices` consumes
LNbits' global payment queue, filters on
`payment.extra.tag == "restaurant"`, and dispatches to
[[order-flow|services.mark_order_paid]].
2. **NostrClient bootstrap**`nostr.nostr_client.NostrClient`
connects to the `nostrclient` extension's internal WebSocket
after a 10s grace.
3. **Nostr sync**`nostr_sync.wait_for_nostr_events` subscribes
to the relevant filters once the client is up. NIP-17 unwrap is
stubbed.
`restaurant_stop` cancels the three tasks and closes the WebSocket.
If `nostrclient` isn't enabled, the publisher / sync no-op gracefully
and the extension still works — it just operates as REST-only without
the [[nostr-layer]].
## Boundaries we keep
The extension only ever knows about **its own restaurant's** data.
There is no global "festival" or "marketplace" entity stored anywhere
in `restaurant.*` tables. Cross-restaurant grouping is the customer
webapp's concern; see [[webapp-integration]].
## See also
- [[data-model]] — the entity catalog
- [[order-flow]] — payment lands → print job
- [[nostr-layer]] — what propagates outward
- [[adr-0001-menu-tree]] — why the menu storage choice