# Sample Extension Template (Runner + Iframe UI)

This template outlines a minimal v2 extension composed of:
- Server handlers compiled to WASM and executed by the Runner
- An iframe UI built with React using the Alga UI kit and SDK
- A Manifest v2 that declares endpoints and the UI entry

Core rules:
- All server calls go through the Gateway: `/api/ext/[extensionId]/[[...path]]` → Runner `POST /v1/execute`
- UI assets are served by the Runner at `${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/[...]`
- Iframe src is constructed via [buildExtUiSrc()](../../../server/src/lib/extensions/ui/iframeBridge.ts:38) and initialized with [bootstrapIframe()](../../../server/src/lib/extensions/ui/iframeBridge.ts:45)
- Gateway scaffold reference: [server/src/app/api/ext/[extensionId]/[[...path]]/route.ts](../../../server/src/app/api/ext/%5BextensionId%5D/%5B%5B...path%5D%5D/route.ts)

## File Structure

```
my-extension/
├── src/
│   ├── http/
│   │   ├── list_agreements.ts
│   │   └── sync.ts
│   └── ui/
│       ├── index.html
│       └── src/
│           ├── main.tsx
│           └── components/
├── dist/
│   ├── handlers/http/list_agreements.wasm   # built artifact(s)
│   └── ui/                                  # built iframe assets
├── manifest.json
├── SIGNATURE                                # generated by CI
├── package.json
└── README.md
```

## Manifest v2

```json
{
  "name": "com.example.agreements",
  "publisher": "Example, Inc.",
  "version": "1.0.0",
  "runtime": "wasm-js@1",
  "capabilities": ["http.fetch", "storage.kv"],
  "ui": { "type": "iframe", "entry": "ui/index.html" },
  "api": {
    "endpoints": [
      { "method": "GET", "path": "/agreements", "handler": "dist/handlers/http/list_agreements" },
      { "method": "POST", "path": "/agreements/sync", "handler": "dist/handlers/http/sync" }
    ]
  },
  "assets": ["ui/**/*"]
}
```

## WASM Handler (conceptual)

```ts
// src/http/list_agreements.ts
export async function list_agreements(ctx) {
  const items = await ctx.storage.list({ namespace: 'agreements' });
  return { status: 200, headers: { 'content-type': 'application/json' }, body: { data: items } };
}
```

Build to `dist/handlers/http/list_agreements.wasm` via your language toolchain.

## Iframe UI (React)

```tsx
// ui/src/main.tsx
import React, { useEffect, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { useExtension, BridgeProvider } from '@alga/extension-iframe-sdk';
import { DataTable } from '@alga/ui-kit';

function App() {
  const { context } = useExtension();
  const [rows, setRows] = useState<any[]>([]);

  useEffect(() => {
    async function load() {
      const res = await fetch(`/api/ext/${context.extensionId}/agreements`, { headers: context.authHeaders });
      const data = await res.json();
      setRows(Array.isArray(data) ? data : data.data ?? []);
    }
    load();
  }, [context.extensionId, context.authHeaders]);

  return <DataTable columns={[{ key: 'name', header: 'Name' }]} rows={rows} />;
}

createRoot(document.getElementById('root')!).render(
  <BridgeProvider>
    <App />
  </BridgeProvider>
);
```

## Packaging & Signing

- Produce bundle with `manifest.json`, WASM artifacts under `dist/`, UI assets under `ui/`
- Compute SHA256 over the canonical bundle
- Sign using your publisher certificate; write `SIGNATURE`
- Publish to the Registry (CI step)

See: [security_signing.md](security_signing.md)

## Install & Use

- Tenant admin installs a specific version and grants capabilities
- UI entry is served by the Runner at `${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/index.html`
- Client/UI code calls handlers via `/api/ext/{extensionId}/...` (Gateway proxies to Runner)

## Notes

- Do not rely on server filesystem paths; artifacts are fetched by content hash
- Keep responses small; paginate lists
- Use SDK‑provided headers for authentication; do not forward end‑user tokens
- Validate inputs; return structured errors

Related references:
- Gateway route scaffold: [server/src/app/api/ext/[extensionId]/[[...path]]/route.ts](../../../server/src/app/api/ext/%5BextensionId%5D/%5B%5B...path%5D%5D/route.ts)
- Iframe bridge: [server/src/lib/extensions/ui/iframeBridge.ts](../../../server/src/lib/extensions/ui/iframeBridge.ts:38)
- Runner overview: [runner.md](runner.md)
- Manifest schema: [manifest_schema.md](manifest_schema.md)
