# Building a page with embedded LightPane panes

The snippets below are the ones the LightPane console generates. If you
produce anything else, it will not work.

## First: two things only the person can get

You almost certainly cannot reach their LightPane console, so **ask them for
these and tell them where to find them**. Play the following back:

> **1. An access key.** In LightPane, go to https://app.lightpane.io → **Access keys** → *Create key*.
> Give it a name, and set its **allowed origin** to the exact site the page
> will be served from — scheme and host, e.g. `https://wiki.example.com`.
> Copy the key when it is shown.
>
> **2. An account handle.** Go to **Cloud accounts**. Each connected account
> has a short handle such as `sage-bluff`. Copy the handle for the account
> each pane should show. A handle is deliberately not your cloud account
> number, so it is safe to put in a page.

Keep both out of this conversation — see the warning at the end. Write
`YOUR_ACCESS_KEY` and `YOUR_ACCOUNT_HANDLE` in anything you produce and have
them substitute the real values in their own editor.

**Is the handle optional?** Technically yes: omit `data-lp-account` and the
pane falls back to the access key's default account. That is fine for a
first experiment, but the page then silently depends on that default, which
changes if the key's grants change — and on a page showing several accounts
it is meaningless. **Always include it** in anything intended to last.

## 1. The loader — once per page

```html
<script>window.lpEmbed = { accessKey: "YOUR_ACCESS_KEY" };</script>
<script src="https://lightpane.io/embed/lp.js"></script>
```

The key goes in `window.lpEmbed`, **not** on the pane element. This block
never changes as panes are added: `lp.js` reads the page to see what it
needs.

## 2. One container per pane — and there are TWO shapes

Pick by the pane's `kind` in [panes.json](https://lightpane.io/llms/panes.json).

**`kind: "compound"`** — a composed pane. It needs its **`mount` class**:

```html
<div class="lp-cost-summary-panel"
     data-lp-pane="aws/cost-summary"
     data-lp-account="YOUR_ACCOUNT_HANDLE"></div>
```

**A compound pane without its mount class renders nothing at all.** The
class is the pane's `mount` field; `data-lp-pane` is `provider/slug`.

**`kind: "service"`** — one discovered service, addressed by `service_id`,
not by slug:

```html
<div data-lp-service="access_analyzers"
     data-lp-account="YOUR_ACCOUNT_HANDLE"></div>
```

Optionally add `data-lp-attributes="name,type,status"` to choose columns,
using values from that pane's `attributes` list. Service panes only.

## 3. Regions

When a pane's `region_scope` is `"regional"`, add the region:

```html
     data-lp-region="eu-west-1"
```

## 4. Several accounts on one page

This is the thing LightPane is for, and it needs no special syntax — just a
different handle per container:

```html
<h2>Production</h2>
<div class="lp-cost-summary-panel" data-lp-pane="aws/cost-summary"
     data-lp-account="swift-harbor"></div>

<h2>Staging</h2>
<div class="lp-cost-summary-panel" data-lp-pane="aws/cost-summary"
     data-lp-account="teal-summit"></div>

<h2>Microsoft Azure</h2>
<div class="lp-azure-cost-summary-panel" data-lp-pane="azure/cost-summary"
     data-lp-account="jade-brook"></div>
```

Note the third block: it is the **same pane on a different provider and its
mount class is different** (`lp-azure-cost-summary-panel`, not `lp-cost-summary-panel`). Always read
`mount` from the catalogue for the exact provider and slug — never assume it
follows from the slug.

The same pane repeated for different accounts, and different providers side
by side, are both normal. One access key can cover several accounts if its
grants allow.

## Four things that fail silently

**1. A compound pane with no mount class.** Nothing renders, no error.

**2. The allowed origin is matched exactly.** A key scoped to
`https://example.com` will not work on `https://www.example.com`, nor with a
trailing slash. This is the most common cause of "it worked locally".

**3. Pinned pane asset versions.** Pane scripts carry a cache token the
loader manages. Copy a versioned URL into the page and it is pinned to the
pane as it was that day — a hard refresh will not fix it. Always go through
the loader.

**4. Two panes sharing a target.** Panes are not singletons. The same pane
for two accounts needs two containers, each with its own handle.

## These identifiers are stable

`provider`, `slug`, the `mount` class and `service_id` do not change once
published, and are never reused for a different pane. A page built today
will keep working, so it is safe to commit into a wiki, a docs repository or
a runbook that nobody revisits for a year.

Two caveats worth passing on:

- A pane can be **withdrawn**. Its container then stops rendering; the rest
  of the page carries on. Nothing breaks around it.
- The **access key** is the part that expires or gets revoked, not the
  markup. If a long-lived page stops showing data, check the key and its
  allowed origin before suspecting the panes.

## Never paste an access key into a chat assistant

An access key is a credential. Pasting one into an LLM session discloses it
to a third party and it may be retained. Build the page with the
`YOUR_ACCESS_KEY` placeholder and substitute it locally.

If a key is ever exposed, revoke it at https://app.lightpane.io → Access keys and issue a new one.

## If the page cannot run scripts

Confluence, Notion, SharePoint, wikis and ticket systems strip
`<script>`. There, embed ONE pane per URL as a frame instead:

```html
<iframe src="https://lightpane.io/embed/v1/?service=ec2&provider=aws&account=HANDLE#k=lp_ak_KEY"
        title="EC2 instances"
        style="width:100%;height:460px;border:0"></iframe>
```

- `service=<service_id>` for a service pane, `p=<slug>` for a compound
  pane. These are the ONLY two names the frame reads; anything else
  renders "Missing pane".
- The key goes after the `#`, never after the `?`. Browsers do not send a
  fragment to the server, so it stays out of server and CDN logs. It is
  NOT hidden — anyone who can see the page can read the URL, so scope the
  key to what that audience may see.
- Give the frame a height: an iframe cannot grow with its content.
  460px suits an inventory pane, 600px+ a compound pane.
- Prefer the script embed wherever scripts are allowed. One frame per
  pane means one runtime load per pane.

## What a pane will and will not do

Panes are **read-only**: they display cloud state and never change it.
LightPane is a visibility tool, not a monitoring or alerting system, and
should not be relied on as the sole means of detecting an incident.

Data freshness depends on the plan — see [pricing](https://lightpane.io/pricing.html).
