# Hosted Views Artifact Hub

Status: proposal
Purpose: target design for governed hosting of live client-side visualization artifacts
Date: 2026-06-01
Related: [`nexuscrm-platform-architecture.md`](nexuscrm-platform-architecture.md), [`l0-platform-substrate.md`](l0-platform-substrate.md), [`frontend-l0-l1-ownership-inventory.md`](frontend-l0-l1-ownership-inventory.md), [`system-tables-permissions.md`](system-tables-permissions.md)

## 1. Executive Summary

NexusCRM should not finish a Salesforce-shaped reports and dashboards
visualization engine as the primary migration path. The proposed target is a
new neutral primitive: **Hosted Views**.

A Hosted View is a tenant-published browser artifact. It can be a dashboard,
chart pack, operational mini-app, data exploration page, or other custom view.
The artifact is authored outside NexusCRM, for example with normal frontend
tooling, CI, or an assisted generator such as Claude Cowork. NexusCRM stores
immutable artifact versions in S3, hosts the active version in an isolated
iframe, gates access through NexusCRM permissions, and provides a narrow
viewer-authority data bridge.

The core contract:

```text
Authoring environment
  -> builds a browser-only artifact bundle
  -> uploads artifact + manifest to NexusCRM
  -> NexusCRM stores versioned assets in S3 and governance metadata
  -> NexusCRM renders the artifact in an isolated iframe
  -> artifact requests data through a postMessage bridge
  -> NexusCRM executes every data request as the current viewer
```

NexusCRM does **not** build artifacts in v1. It does not run tenant-provided
Node, Python, containers, or server-side code. The first runtime is
client-side JavaScript only.

The closest external precedent is Atlassian Forge Custom UI: static resources
hosted by the platform, rendered in an iframe, with a bridge that calls product
APIs as the current user. Salesforce Canvas, Databricks Apps, Snowflake
Streamlit, embedded BI tools, Cloudflare Access, Vercel deployment protection,
and WebContainers all validate parts of the model, but none is the exact
NexusCRM target.

## 2. Motivation

The previous reports and dashboards migration direction tried to make
NexusCRM a clone target for Salesforce reports: translate report XML, persist
query plans, render HTML snapshots, and refresh them. That approach carried
three problems:

- It kept NexusCRM tied to Salesforce report and dashboard semantics even
  though the product direction is moving away from that visualization engine.
- It blurred artifact access and data access. A rendered report snapshot can
  leak rows from the user who refreshed it to another user who can open the
  folder.
- It created a parallel analytics object model (`Report__c`, `Dashboard__c`)
  even though the desired product is no longer "Salesforce reports, but
  hosted elsewhere."

The new product bet is smaller and cleaner:

- Authors build whatever visualization they need outside NexusCRM.
- NexusCRM becomes the governed publishing, hosting, sharing, and audit hub.
- Live data access remains enforced by NexusCRM's permission system for the
  current viewer.

This is an L0 substrate, not a CRM feature. A CRM pack might publish pipeline
dashboards through it. A Jira-like pack might publish sprint health views. A
tenant might publish a customer-specific executive dashboard. The platform
runtime should not know which application vocabulary the artifact visualizes.

## 3. Non-Goals

This design deliberately does not include:

- A Salesforce-compatible report builder.
- A native drag-and-drop dashboard builder.
- Server-side execution of uploaded Node, Python, containers, or shell
  commands inside NexusCRM.
- Cloud builds, dependency installation, or `npm install` inside NexusCRM.
- Owner-authority or publisher-authority data access for v1.
- Public anonymous sharing.
- Arbitrary external network egress from hosted artifacts.
- Reuse of Salesforce `Report` / `Dashboard` as the public product model.

WebContainers may become useful for future browser-based authoring, but they
are not required for v1 hosting.

## 4. Foundation Completion Contract

For this proposal, the base foundation is not a builder, marketplace, or
complete app development platform. The foundation is complete when a real
Hosted View report or dashboard can be uploaded to NexusCRM, shared through a
folder, opened by an allowed viewer, and queried live with every data read
authorized as that viewer through the same RBAC, CRUD, FLS, and RLS paths used
by ordinary NexusCRM APIs.

Required foundation capabilities:

- `HostedView` and `HostedViewVersion` metadata with an active version pointer.
- S3-backed immutable artifact storage for bundles and assets. Server-local
  filesystem storage is not a persistence or serving target for artifacts.
- A Hosted View-specific folder/share substrate, for example
  `_System_HostedViewFolder` and `_System_HostedViewFolderShare`.
- An isolated iframe shell that can render the active artifact version.
- A deployed SDK transport that talks to the parent shell over
  `MessageChannel`/`postMessage`.
- A backend bridge that supports at least `describeObject`, `query`,
  `getRecord`, `getUserContext`, and `logClientEvent`.
- Manifest scope validation, artifact hashing, size/type validation, and
  fail-closed external egress policy.
- Audit events for upload, activation, sharing, open, bridge call, bridge
  denial, and rollback.

The foundation explicitly does not require:

- NexusCRM-side building, dependency installation, or source compilation.
- A browser-based IDE or WebContainers authoring surface.
- A marketplace, template gallery, or cross-tenant discovery model.
- Scheduled refresh, background data pulls, or report snapshots.
- Write operations through the bridge.

Downstream authoring experiences should be able to start after this foundation
exists because their output contract is just a built browser artifact plus
manifest and SDK usage.

## 5. External Precedents

| Product | Relevant Pattern | Lesson For NexusCRM |
| --- | --- | --- |
| Atlassian Forge Custom UI | Platform-hosted static resources in an isolated iframe; `requestJira` bridge calls Jira APIs as the current user; manifest scopes and egress/CSP controls. | Closest architectural precedent. Copy the hosted static artifact + viewer-authority bridge model. |
| Salesforce Canvas | External app embedded in Salesforce with signed request context and OAuth token refresh. | Useful precedent for contextual embedded apps, but avoid handing raw API tokens to uploaded artifact code. |
| Shopify App Bridge / Microsoft Teams tabs | Host shell mediates authenticated embedded app identity and session tokens. | Confirms that host-mediated identity is standard, but NexusCRM should keep the bridge narrower than generic token access. |
| Databricks Apps | Separates app-level permissions from data/resource authorization; supports both app identity and user identity. | Preserve a strict distinction between "can open this Hosted View" and "can read these records." |
| Snowflake Streamlit | Apps are shared through RBAC, but default execution uses owner rights. | This is the main anti-pattern for v1. Viewer-authority must be explicit to avoid data overexposure. |
| Tableau / Power BI / Superset / Metabase / Grafana embedding | Mature embedded analytics products use per-user tokens, row-level security, allowed origins, and/or authenticated embeds. | Embedded analytics security is mostly identity propagation plus explicit RLS; snapshots and guest embeds are easy to mis-secure. |
| Cloudflare Access / Vercel protection | Static or web apps protected by SSO, policy, and audit. | Validates protected hosting and audit, but does not provide NexusCRM data authorization. |
| WebContainers / StackBlitz | Node.js toolchains can run client-side in a browser tab using WebAssembly and Service Workers. | Useful for authoring or preview, but adds browser complexity and should not be required for artifact viewing. |

Reference starting points:

- Atlassian Forge Custom UI static resources:
  `https://developer.atlassian.com/platform/forge/extend-ui-with-custom-options/`
- Atlassian Forge Custom UI iframe:
  `https://developer.atlassian.com/platform/forge/custom-ui/iframe/`
- Atlassian Forge bridge `requestJira`:
  `https://developer.atlassian.com/platform/forge/apis-reference/ui-api-bridge/requestJira/`
- Atlassian Forge permissions and egress:
  `https://developer.atlassian.com/platform/forge/manifest-reference/permissions/`
- Salesforce Canvas framework:
  `https://resources.docs.salesforce.com/latest/latest/en-us/sfdc/pdf/canvas_framework.pdf`
- Databricks Apps key concepts:
  `https://docs.databricks.com/aws/en/dev-tools/databricks-apps/key-concepts`
- Snowflake Streamlit owner's rights:
  `https://docs.snowflake.com/en/developer-guide/streamlit/object-management/owners-rights`
- Metabase embed security:
  `https://www.metabase.com/docs/latest/embedding/securing-embeds`
- Superset embedding:
  `https://superset.apache.org/user-docs/using-superset/embedding/`
- WebContainers introduction:
  `https://webcontainers.io/guides/introduction`

## 6. Layer Ownership

Hosted Views follow the standard NexusCRM layer model.

### L0 Owns

L0 owns only generic substrate:

- Artifact upload, validation, immutable storage, and version selection.
- Isolated artifact hosting.
- The viewer-authority bridge contract.
- Runtime enforcement of artifact access, manifest scopes, CRUD/FLS/RLS, rate
  limits, and egress policy.
- Audit events for upload, activation, access, bridge calls, denies, and
  rollbacks.
- Generic permission primitives used to share Hosted Views.

L0 must not know whether a Hosted View is a CRM pipeline dashboard, an ITSM
incident dashboard, a Jira-like sprint board, or a tenant-specific executive
page.

### L1 Owns

An app pack may ship default Hosted View templates or seed example artifacts
when those artifacts are product defaults. Those artifacts are L1 payload, not
platform code.

If a CRM pack ships a "Pipeline Overview" Hosted View, CRM owns that artifact's
content, object references, copy, and visualization choices. L0 only hosts and
enforces the contract.

### L2 Owns

Most Hosted Views are tenant/admin/user-authored metadata. They belong to L2:

- Uploaded artifact versions.
- Folder placement and sharing policy.
- Manifest-declared requested data scopes.
- Approvals, publication status, and tenant-specific naming.
- Tenant-specific asset import approvals or future bridge-governed connector
  egress policy, if the platform later supports them.

### L3 Owns

Record rows returned through the data bridge remain L3. The artifact never owns
record data merely because it renders that data.

## 7. Vocabulary

The user-facing name is **Hosted View**.

The name is intentionally broader than "report" or "dashboard":

- A dashboard is a common Hosted View, not the primitive itself.
- A report is a Salesforce-shaped analytics concept and is not the target
  model.
- "App" is also too broad because v1 does not run server-side application code.

Implementation names should stay neutral. Candidate table/object names:

- `_System_HostedView`
- `_System_HostedViewVersion`
- `_System_HostedViewAsset`
- `_System_HostedViewBridgeAudit`
- `_System_HostedViewFolder`
- `_System_HostedViewFolderShare`

The v1 folder substrate should be Hosted View-specific. Reusing Salesforce
`ReportFolder` as the public SSoT is not part of the target design. A generic
`_System_Folder` substrate may replace or absorb Hosted View folders later, but
that should be a generic platform convergence rather than a dependency of the
base foundation.

## 8. Artifact Contract

A Hosted View version is an immutable bundle plus a manifest.

### Bundle

The v1 bundle format is a zip of browser assets:

- `index.html` or another manifest-declared entrypoint.
- JavaScript, CSS, fonts, images, JSON, WASM, and other static assets.
- No server-side entrypoint.
- No executable install step.
- No symlinks.
- No absolute paths or `..` traversal paths.
- No external asset dependency by default.

### Storage

Artifact persistence is S3-backed. NexusCRM may store a canonical uploaded zip,
expanded content-addressed asset objects, or both, but production and shared
environment artifact storage should use S3-compatible object storage.

The server process must not persist or serve Hosted View versions from local
filesystem paths. Local filesystem use is limited to author workstations before
upload and unit-test fakes that do not represent the product storage contract.
If a server framework temporarily buffers an upload to disk, that buffer is
transient request data, must be deleted before the request completes, and must
never become the artifact store.

Database metadata records:

- Artifact digest.
- Manifest digest.
- S3 bucket/key/version identifiers for the canonical bundle and/or expanded
  assets.
- Uploader user id.
- Upload session/request metadata.
- Created-by tool, if supplied.
- Optional source digest, for example a Git commit or source archive digest.
- Size, asset count, MIME inventory, and validation status.

### Manifest

Example:

```json
{
  "kind": "nexus.hosted_view",
  "manifestVersion": 1,
  "name": "pipeline-region-dashboard",
  "displayName": "Pipeline by Region",
  "entrypoint": "index.html",
  "runtime": "browser",
  "dataAuthority": "viewer",
  "requestedApiScopes": [
    "describe:Opportunity",
    "query:Opportunity",
    "query:Account"
  ],
  "networkPolicy": "nexus-bridge-only",
  "createdByTool": "nexus-cli",
  "sourceDigest": "sha256:...",
  "artifactDigest": "sha256:..."
}
```

Manifest rules:

- `dataAuthority` must be `viewer` in v1.
- `runtime` must be `browser` in v1.
- `requestedApiScopes` are an upper bound, not a permission grant.
- The current viewer still needs object, field, row, and endpoint permission.
- `networkPolicy` defaults to `nexus-bridge-only`.
- External origin URLs are denied at runtime. Future external assets must be
  imported or proxied through a NexusCRM-controlled, content-addressed asset
  path before the artifact can reference them.

## 9. Runtime Isolation

Uploaded artifact JavaScript is untrusted tenant code. It must not run with
NexusCRM origin privileges.

Target isolation:

```text
NexusCRM shell origin
  renders chrome, permissions, navigation, bridge controller

Hosted View artifact origin or opaque sandbox origin
  renders tenant bundle in iframe
  no cookies
  no local NexusCRM storage
  no direct bearer token
  no direct same-origin API access
```

V1 iframe policy should start fail-closed:

```html
<iframe
  sandbox="allow-scripts"
  referrerpolicy="no-referrer"
  ...>
</iframe>
```

Do not add `allow-same-origin`, `allow-top-navigation`,
`allow-popups`, `allow-forms`, or `allow-downloads` by default. Each new flag
needs a specific product reason and a security review.

The artifact CSP should default to:

- `default-src 'self'` for versioned artifact assets.
- `connect-src 'none'` for direct network calls.
- no external `script-src`, `style-src`, `font-src`, `img-src`, or `frame-src`
  entries in v1.

Future static-resource support must still avoid browser egress from the
artifact. The safe shape is content-addressed import at publish time, or a
NexusCRM-controlled proxy/cache that serves only predeclared immutable assets
by digest. The artifact must not be able to choose an arbitrary external URL,
path, query string, or header at render time.

If the sandbox produces an opaque `null` origin, parent/iframe communication
must not trust `event.origin` alone. Use a `MessageChannel` plus an
unpredictable one-time bridge nonce bound to the Hosted View version and frame
instance.

## 10. Viewer-Authority Bridge

The bridge is the only supported live data path for hosted artifacts.

The artifact must not call `/services/data`, `/api`, or any other NexusCRM
endpoint directly. Instead it sends structured bridge requests to the parent
shell. The parent shell calls a backend bridge endpoint using the already
authenticated browser session. The backend evaluates:

1. The viewer can open the Hosted View.
2. The active Hosted View version is the version making the request.
3. The requested bridge operation is allowed by the manifest.
4. The viewer has the required API/system permission.
5. The viewer has CRUD/FLS/RLS access for every object, field, and row.
6. The request satisfies per-viewer and per-artifact rate limits.

The bridge returns only the allowed data. It does not return a reusable access
token to the iframe.

### Initial Bridge Surface

V1 should expose a small operation set:

| Operation | Purpose | Enforcement |
| --- | --- | --- |
| `describeObject` | Read object/field metadata for rendering controls. | Viewer setup/data permissions plus manifest scope. |
| `query` | Run SOQL or a constrained query payload as the viewer. | CRUD/FLS/RLS, max rows, timeout, object/field extraction, manifest scope. |
| `getRecord` | Fetch one record by object/id. | CRUD/FLS/RLS and field allowlist. |
| `getUserContext` | Return safe viewer context such as user id, display name, locale, timezone. | Never returns session tokens or permission internals. |
| `logClientEvent` | Record client-side load/render/error telemetry. | Size limits and sampling. |

Later bridge operations may include aggregates, UI API shapes, standard action
invocation, file downloads, and write operations. They should not be admitted
until read-only Hosted Views are proven safe.

### Scope Semantics

Manifest scopes restrict the artifact. They do not grant the viewer access.

For example:

```text
manifest: query:Opportunity
viewer:   no Opportunity read permission
result:   bridge returns 403
```

```text
manifest: no query:Case
viewer:   has Case read permission
result:   bridge returns 403
```

This dual gate is the heart of the model. It prevents both over-broad artifacts
and over-broad viewers.

## 11. Permission Model

Hosted View access and data access are separate.

### Artifact Access

Artifact access answers:

- Can this user see the Hosted View in navigation/search/folders?
- Can this user open the Hosted View shell?
- Can this user upload a draft version?
- Can this user activate a version?
- Can this user manage sharing or delete/archive the Hosted View?

Target access levels:

| Level | Meaning |
| --- | --- |
| `View` | Open the Hosted View shell and request data through allowed bridge operations. |
| `Edit` | Upload draft versions and edit metadata. |
| `Publish` | Activate a version for viewers. |
| `Manage` | Change sharing, archive, rollback, and transfer ownership. |

The exact table shape can be direct grants, folders, or both. The target should
reuse generic permission primitives rather than Salesforce report-folder names.

### Data Access

Data access answers:

- Can this viewer query this object?
- Can this viewer see these fields?
- Can this viewer see these rows?
- Can this viewer invoke this action?

Every bridge data call must use the same authoritative permission and RLS paths
as ordinary NexusCRM APIs. There is no Hosted View-specific data bypass.

### No Publisher Authority In V1

The publisher's authority applies only to publishing. It does not authorize
data reads for viewers.

This rejects the Snowflake Streamlit owner-rights pattern for v1. It also
avoids report-snapshot leakage where a broad-access user refreshes data and a
narrow-access user later sees the result.

If a future product needs "run as publisher" or "run as service account", that
must be a separate authority mode with explicit UI labeling, audit, approval,
and likely a different permission gate.

## 12. Upload And Publication Flow

### JavaScript Authoring And SDK

Claude Cowork should be treated as one possible generator, not the durable
platform contract. A Hosted View can be hand-written, generated by an AI tool,
checked into Git, built in CI, or produced by any other frontend workflow. The
stable contract is JavaScript source that builds to a browser bundle,
`nexus.hosted_view` manifest, and SDK calls.

The recommended SDK shape is `@nexuscrm/hosted-view-sdk`. Artifact code calls
the SDK instead of calling NexusCRM REST endpoints directly:

```ts
import { nexus } from "@nexuscrm/hosted-view-sdk";

const viewer = await nexus.getUserContext();

const result = await nexus.query({
  soql: "SELECT Id, Name, Amount FROM Opportunity LIMIT 50"
});
```

The SDK owns the transport:

| Transport | Where It Runs | Behavior |
| --- | --- | --- |
| `LocalDevTransport` | Local development server | SDK calls a local dev proxy, which forwards to a configured NexusCRM tenant using developer auth and the same bridge-shaped request contract. |
| `BridgeTransport` | Deployed Hosted View iframe | SDK talks to the parent shell over `MessageChannel`; the parent shell calls the backend bridge as the current viewer. |
| `MockTransport` | Unit tests and offline demos | SDK returns fixture data without NexusCRM auth. |

The local dev proxy is the API shape translator. It lets developers write the
same SDK calls locally and after deployment while hiding whether the transport
is a local proxy or the iframe bridge. Local auth can use browser OAuth, an
existing NexusCRM session, or a short-lived dev token held by the proxy. Tokens
must never be embedded in the built artifact.

Expected local workflow:

```bash
npm create nexus-hosted-view pipeline-dashboard
cd pipeline-dashboard
nexus hosted-view dev --target https://staging.example.com
nexus hosted-view build
nexus hosted-view publish --folder Sales/Pipeline
```

NexusCRM stores the result but does not run the build.

### NexusCRM Upload API

Upload should separate draft creation, artifact transfer, validation, and
activation:

1. Create a draft version with manifest and bundle.
2. Accept artifact bytes through a backend-streamed upload or pre-signed S3
   upload.
3. Validate, hash, scan, and store all assets immutably in S3.
4. Activate the version only after validation passes and the caller has
   publish rights.

Activation changes the active version pointer; it does not mutate the version.
Rollback is pointer movement to a previous validated version.

### Validation

Minimum validation:

- Manifest parses and matches expected schema.
- Bundle contains declared entrypoint.
- Bundle paths are normalized and safe.
- Bundle MIME types are known or explicitly allowed.
- Bundle size and asset count are under tenant limits.
- No symlinks or traversal paths.
- Declared artifact digest matches computed digest when supplied.
- Requested scopes are syntactically valid.
- `dataAuthority`, `runtime`, and `networkPolicy` are v1-supported values.
- External resource declarations are absent unless a future content-addressed
  import/proxy substrate exists.

Optional but recommended:

- Dependency/SBOM upload.
- Known-vulnerable package scan.
- Secret scanning of bundled assets.
- Static HTML/JS scan for obvious token exfiltration patterns. This is defense
  in depth only; isolation and egress controls remain the real boundary.

## 13. Audit And Compliance

Hosted Views need full audit because they host untrusted code that may render
sensitive record data.

Required audit events:

| Event | Minimum Fields |
| --- | --- |
| `hosted_view.created` | view id, owner, creator, folder, request id |
| `hosted_view.version.uploaded` | view id, version id, uploader, artifact digest, manifest digest, size |
| `hosted_view.version.validation_failed` | version id, validator, failure code |
| `hosted_view.version.activated` | view id, previous version, new version, publisher |
| `hosted_view.version.rolled_back` | view id, from version, to version, actor |
| `hosted_view.permission.changed` | view id/folder id, actor, old grant, new grant |
| `hosted_view.opened` | view id, version id, viewer, user agent, request id |
| `hosted_view.bridge.call` | view id, version id, viewer, operation, object set, row count, latency, status |
| `hosted_view.bridge.denied` | view id, version id, viewer, operation, denial reason |
| `hosted_view.client_error` | view id, version id, viewer, error fingerprint, sampled details |

Bridge audit may be high-volume. The platform should support both sampled
success telemetry and complete deny/failure telemetry. Security-sensitive
deployments may require full per-request logging.

Audit rows that may carry L3 payload or query text should follow the same
read-gate discipline documented in
[`system-tables-permissions.md`](system-tables-permissions.md).

## 14. Network And Egress Policy

Default v1 policy: `nexus-bridge-only`.

Under that policy:

- Artifact JavaScript cannot call arbitrary external endpoints.
- Artifact JavaScript cannot directly call NexusCRM APIs.
- All data access goes through the parent bridge.
- All artifact assets are served from the stored bundle.

There is no safe "external static resource but not data egress" exception for
untrusted iframe JavaScript. If `img-src`, `font-src`, `style-src`, or any
other browser fetch path points at an external origin, the artifact can encode
viewer-authorized record data into the URL, request body, headers, referrer, or
timing pattern. The platform must treat every runtime external request from an
artifact as data egress.

Future external asset support therefore needs a NexusCRM-controlled import or
proxy/cache model, not raw browser egress:

```json
{
  "networkPolicy": "nexus-bridge-only",
  "importedAssets": [
    {
      "kind": "image",
      "sourceOrigin": "https://cdn.example.com",
      "sourcePath": "/logos/acme.svg",
      "sha256": "sha256:...",
      "servedAs": "assets/imported/acme-logo.sha256.svg",
      "reason": "company logo asset"
    }
  ]
}
```

Under this model NexusCRM fetches or verifies the asset at publish time, stores
it with the Hosted View version, and serves it from the same immutable artifact
asset surface as every other bundle file. A future runtime proxy must be
equally constrained: only predeclared content-addressed assets, no
artifact-chosen URL components, no viewer-specific query/body/header material,
and no forwarding of NexusCRM cookies or bearer tokens.

Any future feature that lets record data leave NexusCRM must be modeled as an
explicit bridge-governed connector/callout surface with scopes, permissions,
rate limits, and audit. It must not be smuggled in through CSP resource
exceptions.

## 15. Caching And Freshness

Hosted Views are live by default because they query through the bridge as the
viewer.

Permitted caches:

- Browser cache for immutable artifact assets, keyed by version digest.
- Parent-shell cache for describes and other non-sensitive metadata, scoped to
  viewer and tenant.
- Short-lived bridge response cache only when the response is viewer-scoped,
  object/field scoped, and invalidated by the same data-change events used by
  ordinary APIs.

Do not cache query results globally across viewers in v1. A global result cache
can reintroduce the snapshot leakage problem this design is meant to avoid.

## 16. Failure Modes

Failure should be explicit and non-leaky:

- If artifact access is denied, do not load the iframe.
- If bridge data access is denied, return a structured 403 to the artifact and
  audit the denial.
- If the active version fails to load, show a shell-owned error state and link
  managers to version history.
- If a bridge call exceeds limits, return a structured limit error and audit
  the limit event.
- If artifact JavaScript throws, the parent shell should record a client-error
  event without exposing session or token data.

The iframe should never be allowed to navigate the parent, rewrite NexusCRM
chrome, or prompt the user for NexusCRM credentials.

## 17. Risks And Mitigations

| Risk | Why It Matters | Mitigation |
| --- | --- | --- |
| XSS through uploaded artifact code | Untrusted JS could attack the host page if same-origin or over-permissioned. | Separate/opaque origin, `sandbox="allow-scripts"` only, no cookies/tokens in iframe, bridge nonce. |
| Data exfiltration | Viewer-authorized data can be leaked to external endpoints, including image/font/style URLs. | `connect-src 'none'`, no direct API calls, no external runtime origins, content-addressed asset import only if added later. |
| Permission confusion | Users may think folder access implies data access or vice versa. | Two-tier permission model; UI labels artifact access separately from data authority. |
| Snapshot leakage reintroduced by caching | Broad-access viewer data could be cached and shown to narrow-access viewer. | No cross-viewer result cache in v1; every bridge call runs as current viewer. |
| Query abuse and cost | Arbitrary dashboards can spam expensive queries. | Manifest scopes, query analyzer, max rows, timeout, rate limits, per-artifact quotas. |
| Supply-chain attacks | Authoring tools or npm dependencies can bundle malicious code. | Immutable versions, digests, source provenance, optional SBOM, secret scanning, rapid disable/rollback. |
| Shadow-app sprawl | Users publish many hard-to-govern mini-apps. | Ownership, folders, lifecycle states, last-used metadata, stale-view review, manager inventory. |
| Browser compatibility | Strict sandboxing and future WebContainers can break some client code. | Keep v1 runtime plain browser assets; document unsupported APIs; test common browsers. |
| Audit volume | Per-query audit can be noisy. | Complete deny/failure audit, sampled success audit by default, full mode for regulated tenants. |

## 18. V1 Scope

V1 should include:

- `HostedView` and immutable version metadata.
- Zip bundle upload and validation.
- S3-backed immutable artifact storage.
- Active version pointer plus rollback.
- Isolated iframe viewer.
- Bridge operations: `describeObject`, `query`, `getRecord`,
  `getUserContext`, `logClientEvent`.
- Viewer-authority enforcement for every data call.
- Manifest scopes as upper-bound restrictions.
- Hosted View-specific folder/direct sharing for `View`, `Edit`, `Publish`,
  `Manage`.
- Audit events listed in this document.
- SDK, CLI, and local upload path for built artifacts.

V1 should not include:

- Server-side uploaded code execution.
- NexusCRM build pipeline.
- Scheduled refresh jobs.
- Background data pulls.
- External runtime egress.
- Public anonymous sharing.
- Write operations through the bridge.
- Marketplace/discovery UX beyond a tenant-local list.

## 19. Implementation Implications

This design implies new generic platform substrate. It should not be
implemented by extending `Report`, `Dashboard`, `DashboardComponent`, or
Salesforce analytics handlers.

Expected future implementation areas:

- Backend hosted-view feature module for artifact/version lifecycle.
- S3-compatible object storage integration for immutable assets; no
  server-local filesystem artifact serving.
- Permission service integration for Hosted View access.
- Bridge REST endpoint or internal handler that executes as the current user.
- Audit event definitions and retention policy.
- Frontend Hosted View shell, iframe host, and bridge controller.
- Admin/list UI for Hosted Views, version history, and sharing.
- SDK and CLI tooling that keep local development on the same logical bridge
  contract as deployed artifacts.

Do not put application-specific visualization logic in L0. The frontend shell
hosts the artifact; it does not know what the artifact renders.

## 20. Open Decisions

| Decision | Options | Recommendation |
| --- | --- | --- |
| Generic folder convergence | Keep Hosted View-specific folders or later migrate to a generic `_System_Folder` substrate | V1 uses Hosted View-specific folders. Decide generic folder convergence after the base foundation is proven. |
| Query bridge shape | Raw SOQL, structured query JSON, or both | Start with SOQL plus strict parser/analyzer because NexusCRM already exposes SOQL; add structured helpers later. |
| Artifact approval | Immediate publish by users with `Publish`, or draft/approval workflow | Immediate publish for v1 internal tenants; approval workflow as compliance extension. |
| External assets | No external assets forever, content-addressed import/proxy, or full external fetch policy | No runtime external origins in v1; later allow only NexusCRM-controlled content-addressed asset import/proxy before considering any bridge-governed data egress. |
| Source provenance | Optional source digest, required source bundle, or Git/build-tool integration | Require artifact digest; optional source digest in v1; Git and assisted-generator provenance later. |
| WebContainers | Authoring only, preview runtime, or hosted runtime | Authoring/preview only. Do not require WebContainers for viewing. |
| Write bridge | Never, future explicit scopes, or app-specific actions only | Defer. Reads prove the model first. |

## 21. Review Checklist

Any implementation PR for this design should answer:

1. Does uploaded code ever receive NexusCRM cookies, bearer tokens, local
   storage, or same-origin access?
2. Can every bridge data call be traced to the current viewer, Hosted View id,
   version id, operation, object set, and request id?
3. Are artifact access checks and data access checks separate?
4. Are manifest scopes enforced as an upper bound?
5. Are CRUD, FLS, and RLS enforced through the same SSoT paths as normal APIs?
6. Can an artifact call external network destinations?
7. Can a viewer see cached data computed for another viewer?
8. Can managers disable or roll back a malicious version immediately?
9. Are all high-risk audit rows gated as sensitive data?
10. Does the implementation remain neutral to CRM, Jira-like, ITSM, and tenant
    custom app vocabulary?
11. Are artifact bytes stored in S3-backed immutable storage rather than local
    server filesystem paths?
12. Does local development use the same logical bridge authorization semantics
    as deployed iframe execution?
