# Published Resource Architecture

> **Platform-wide abstraction** for anything that can be published publicly.  
> Normative decision: [ADR-022](adr/ADR-022.md)  
> Experience ownership: [ADR-016](adr/ADR-016.md) · Roadmap: [experience-roadmap.md](../experience-roadmap.md)  
> Contracts: [published-resource-contracts.md](../reference/published-resource-contracts.md)

**Status:** Accepted · **ARCHITECTURE COMPLETE** (documentation freeze — **no implementation in this patch**).

> Final freeze adds: **Published Actions**, **Public Access Model**, **Universal Embed Architecture** (additive, backward compatible).

## 1. Purpose

TorbatYar products publish many public surfaces (sites, pages, forms, menus, cards, events, …). Without a shared abstraction, each vertical and future product invents its own public identity, URL scheme, and cross-service coupling.

**Published Resource** is the official platform abstraction for every present and future public surface. All consumers (Payment, Communication, Short Link, QR, Analytics, CRM, verticals, future products) integrate via **`publish_id` only** — never via Experience (or other) internal tables.

## 2. Published Resource

A **Published Resource** represents anything that can be published publicly for a tenant.

| Field | Meaning |
| --- | --- |
| `publish_id` | Stable public identity (UUID). Cross-service foreign key substitute. |
| `tenant_id` | Owning tenant ([ADR-003](adr/ADR-003.md)) |
| `resource_type` | Extensible type key (string registry — **not** a closed enum in architecture) |
| `resource_id` | Owning-service aggregate id (opaque to consumers) |
| `canonical_url` | Canonical public URL path or absolute URL |
| `publish_status` | Lifecycle: `draft` · `published` · `unpublished` · `archived` (extensible) |
| `visibility` | e.g. `public` · `unlisted` · `tenant_only` (extensible) |
| `owner_service` | Service that owns the aggregate (`experience`, `hospitality`, …) |

### Resource type registry (open)

Architecture **must not hardcode** a finite type list. Types are registered as string keys. Illustrative examples (non-exhaustive):

| Example `resource_type` | Typical owner |
| --- | --- |
| `site` | experience |
| `page` | experience |
| `landing_page` | experience |
| `form` | experience |
| `survey` | experience |
| `appointment` | experience |
| `restaurant_menu` | hospitality (surface may publish via Experience) |
| `product_catalog` | marketplace / ecommerce |
| `campaign` | experience / marketing consumers |
| `digital_business_card` | experience / future Torbat Card |
| `event` | experience / future Torbat Event |
| `booking_page` | experience / future Torbat Booking |
| *future.*\* | any registered service |

Unlimited future types are allowed without ADR revision when they follow this contract.

## 3. Publish Target Registry

The **Publish Target Registry** is the logical catalog of all Published Resources.

Rules:

1. Every publicly reachable surface that other services may attach to **MUST** have a registry entry with a `publish_id`.
2. Registry entries are **tenant-scoped**.
3. Consumers resolve and attach using `publish_id` (+ `tenant_id` for isolation checks).
4. Registry ownership for Experience-authored surfaces is Experience (`experience_db`). Other owner services may publish their own surfaces into the same logical contract (API/events), never by writing Experience tables.
5. **No implementation in this documentation patch** — schema, API, and migrations are deferred to future registered phases.

Logical record:

```
PublishTarget {
  publish_id
  tenant_id
  resource_type
  resource_id
  canonical_url
  publish_status
  visibility
  owner_service
  # optional metadata (locale, title_ref, …) — additive only
}
```

## 4. Independent Resources

The following Experience resource kinds **MUST NOT** require a parent Site or Page to exist or publish:

| Resource | Standalone publish | Optional Site binding |
| --- | --- | --- |
| **Form** | Required | Allowed |
| **Survey** | Required | Allowed |
| **Appointment** (appointment page / booking shell) | Required | Allowed |

Rules:

1. Forms, Surveys, and Appointments are first-class publishable resources.
2. They **may** optionally belong to a Site (and/or Page) for composition.
3. They **must** support standalone publishing with their own `publish_id` and canonical URL.
4. UI and API designs must not force “create Site → create Page → attach Form” as the only path.

Sites and Pages remain publishable resources; independence applies to Form / Survey / Appointment aggregates specifically.

## 5. Cross-Service Integration Contracts

All integrations are **documentation contracts only** until a registered implementation phase.

| Consumer | Attachment | Reference key |
| --- | --- | --- |
| Payment (Torbat Pay) | Paid form / appointment / booking / registration / event | `publish_id` |
| Communication | SMS/email/OTP/notify after submit or booking | `publish_id` |
| Short Link | Resolve short codes to public surfaces | `publish_id` |
| QR Code | Encode / resolve posters, tables, cards, events | `publish_id` |
| Analytics | Pageviews, conversions, funnels | `publish_id` |
| CRM | Lead capture / contact refs from public surfaces | `publish_id` |
| Booking | Scheduling refs attached to public bookable surfaces | `publish_id` |
| Marketplace | Catalog embed / campaign surfaces | `publish_id` |
| Hospitality | Menu / venue public surfaces | `publish_id` |
| Delivery | Campaign / logistics announcement surfaces | `publish_id` |
| Loyalty | Offer / card / campaign surfaces | `publish_id` |
| Sports Center | Membership / event / booking surfaces | `publish_id` |
| Future services | Any public attachment | `publish_id` |

**Forbidden:** consumers must not query Experience (or other) internal tables, import models, or store foreign primary keys as substitutes for `publish_id` when the public surface is the integration target.

Detail: [published-resource-contracts.md](../reference/published-resource-contracts.md).

## 6. Standard Public URL Patterns (Reserved)

Routing is **not implemented** in this patch. The following path prefixes are **reserved** for public resolution:

| Prefix | Intended use (illustrative) |
| --- | --- |
| `/s/` | Site / published site shell |
| `/p/` | Page / landing |
| `/f/` | Form |
| `/survey/` | Survey |
| `/book/` | Appointment / booking page |
| `/menu/` | Restaurant / digital menu surface |
| `/card/` | Digital business card |
| `/event/` | Event surface |

Additional prefixes may be reserved later without breaking this architecture. Canonical URLs stored on the Publish Target Registry should align with these patterns when applicable.

## 7. Future Products

The following commercial products are **architecture-reserved**. They consume Published Resources; they **never own** Experience data:

| Product | Role |
| --- | --- |
| **Torbat Link** | Short / branded link layer over Published Resources |
| **Torbat QR** | QR issuance and resolution to `publish_id` |
| **Torbat Card** | Digital business card surfaces |
| **Torbat Booking** | Booking UX over appointment / bookable resources |
| **Torbat Ticket** | Ticketing over event / paid registration resources |
| **Torbat Event** | Event publishing and discovery |
| **Torbat Learning** | Learning / knowledge page surfaces |

These products integrate through API/Events + `publish_id`. Experience remains owner of page/site/theme/form aggregates it already owns ([ADR-016](adr/ADR-016.md)).

## 8. Payment Integration

Payment ([ADR-020](adr/ADR-020.md), [ADR-021](adr/ADR-021.md)) **may attach** to any Published Resource.

Examples: Paid Form · Paid Appointment · Paid Booking · Paid Registration · Paid Event.

Contract: Payment request / checkout session stores **`publish_id`** (and `tenant_id`). Payment must not own Experience aggregates or read `experience_db`.

## 9. Communication Integration

Communication ([ADR-012](adr/ADR-012.md)) triggers may fire on Published Resource lifecycle or submission events.

Examples: SMS after submit · Email after booking · OTP · Notifications · Campaign messages.

Contract: templates and dispatches reference **`publish_id`** (plus channel/template keys owned by Communication). Providers remain Communication-owned.

## 10. Short Link Integration

Short Link ownership is **reserved** as a future/adjacent product capability (e.g. Torbat Link).

- Short codes resolve to Published Resources via `publish_id`.
- No direct dependency on Experience internals.
- Experience must not embed a competing short-link engine as source of truth.

## 11. QR Integration

QR codes resolve to Published Resources via `publish_id`.

Examples: Restaurant Table · Business Card · Poster · Event · Form · Appointment.

QR issuance may live in a future Torbat QR product or vertical adapters; payload always targets the Publish Target Registry.

## 12. Analytics

Analytics (Experience shells and future platform analytics) record **`publish_id`** for public funnel metrics.

- No ownership change: Experience analytics shells remain Experience-owned COUNT/local shells where already defined.
- Cross-service analytics consumers also key off `publish_id`, not internal page/form table ids.

## 13. Ownership & Boundaries

| Concern | Owner |
| --- | --- |
| Publish Target Registry for Experience-authored surfaces | Experience (`experience`) |
| Page / site / theme / template / form / survey / appointment shells | Experience |
| Publishing, presentation, rendering metadata, embed metadata | Experience |
| Published Action *presentation bindings* | Experience (refs `action_key`) |
| Published Action *business execution* | Owning service of the action |
| Public Access Policy metadata | Experience |
| Authentication / SSO | Identity / Core |
| Payment capture / ledger | Payment |
| Membership gates | Loyalty |
| OTP / messaging | Communication |
| Menu/item catalog source of truth | Hospitality |
| Product catalog source of truth | Marketplace / Ecommerce |
| Media binaries | File Storage |
| Short Link / QR issuance engines | Reserved products |
| Public URL edge routing | Platform edge / frontend (reserved patterns) |
| Builder UI | Frontend ([ADR-002](adr/ADR-002.md)) |

All cross-service attachments continue to reference **`publish_id` only**.

## 14. Published Actions

Every Published Resource may expose **zero or more Actions**. Actions are **service-independent** contracts registered in an open **Action Registry**.

| Field | Meaning |
| --- | --- |
| `action_key` | Open string key |
| `display_name` | Label |
| `owning_service` | Executes business logic |
| `permission` | Invocation permission |
| `capability` | Discovery key |
| `feature_toggle` | Optional enablement |
| `supported_resource_types` | Open type list |

Illustrative keys (not closed): View, Submit, Book, Reserve, Pay, Donate, Register, Download, Open Chat, Send Message, Share, Copy Link, Generate QR, Scan QR, Call, Navigate, Add To Calendar, Automation Trigger, Webhook Trigger, …

Future services register new actions **without modifying Experience**. Normative contract: [published-action-registry.md](../reference/published-action-registry.md).

## 15. Public Access Model

A **Public Access Policy** may compose open strategies, including: Public, Private, Authenticated, Tenant Members, Role Based, Password Protected, OTP Protected, Paid Access, Invitation Only, Signed URL, Temporary Link, Expiration, Rate Limited, Geo Restricted (reserved), and future strategies.

| Must not live in Experience | Owner |
| --- | --- |
| Authentication | Identity / Core |
| Payment unlock | Payment |
| Membership evaluation | Loyalty |
| OTP delivery | Communication |

Normative contract: [public-access-contract.md](../reference/public-access-contract.md).

## 16. Universal Embed Architecture

Published Resources may embed into external sites/apps. Reserved embed types (open): Iframe, JavaScript SDK, Widget, Popup, Inline, Modal, Floating Button, future types.

Embed contracts cover: responsive sizing, security/sandbox, origin validation, permission delegation, token passing, theming inheritance, analytics propagation (`publish_id`).

Normative contract: [embed-contract.md](../reference/embed-contract.md).

## 17. Capability Discovery (additive, docs only)

Future `/capabilities` (or equivalent discovery) MAY advertise:

- `published_resources: true`
- `published_actions: true` (registry available)
- `public_access_policies: true`
- `universal_embed: true`
- Open lists of known `action_key` / `embed_type` / access strategy keys

No API change in this patch — discovery additions are reserved for implementation phases.

## 18. Architecture Complete

With Published Resources, Publish Target Registry, Published Actions, Public Access Model, and Universal Embed Architecture documented and accepted under [ADR-022](adr/ADR-022.md), the **Experience Platform architecture is ARCHITECTURE COMPLETE**.

Remaining work is implementation (backend registry/APIs, frontend builders/public shells) in **future registered phases** — not architecture redesign.

## 19. Non-Goals (Architecture Patches)

- No backend schema, migrations, or APIs
- No frontend UI, embed SDK, or public routers
- No Short Link / QR / Card / Booking product implementation
- No changes to completed Experience phases 11.0–11.10 business code
- No hardcoded closed enums of resource types, actions, access strategies, or embed types

## 20. Related Documents

- [ADR-022](adr/ADR-022.md)
- [Published Resource Contracts](../reference/published-resource-contracts.md)
- [Published Action Registry](../reference/published-action-registry.md)
- [Public Access Contract](../reference/public-access-contract.md)
- [Embed Contract](../reference/embed-contract.md)
- [Module Boundaries](module-boundaries.md)
- [Integration Architecture](integration-architecture.md)
- [Experience Roadmap](../experience-roadmap.md)
- [Payment Contracts](../reference/payment-contracts.md)
- [Services Contracts](../reference/services-contracts.md)